diff --git a/Makefile b/Makefile index d3f42b76c..b7f8fe971 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # # Common makefile for SCSI target mid-level and its drivers # -# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -50,6 +50,7 @@ SRP_DIR=srpt SCST_LOCAL_DIR=scst_local MVSAS_DIR=mvsas_tgt FCST_DIR=fcst +EMULEX_DIR=emulex ISCSI_DIR=iscsi-scst @@ -94,6 +95,12 @@ help: @echo " iscsi_install : ISCSI target: install" @echo " iscsi_uninstall : ISCSI target: uninstall" @echo "" + @echo " emulex : make Emulex target" + @echo " emulex_clean : Emulex target: clean " + @echo " emulex_extraclean : Emulex target: clean + clean dependencies" + @echo " emulex_install : Emulex target: install" + @echo " emulex_uninstall : Emulex target: uninstall" + @echo "" @echo " lsi : make LSI MPT target" @echo " lsi_clean : lsi target: clean " @echo " lsi_extraclean : lsi target: clean + clean dependencies" @@ -148,6 +155,7 @@ all: @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi + @if [ -d $(EMULEX_DIR) ]; then cd $(EMULEX_DIR) && $(MAKE) $@; fi install: cd $(SCST_DIR) && $(MAKE) $@ @@ -159,6 +167,7 @@ install: @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi + @if [ -d $(EMULEX_DIR) ]; then cd $(EMULEX_DIR) && $(MAKE) $@; fi uninstall: cd $(SCST_DIR) && $(MAKE) $@ @@ -170,6 +179,7 @@ uninstall: @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi + @if [ -d $(EMULEX_DIR) ]; then cd $(EMULEX_DIR) && $(MAKE) $@; fi clean: cd $(SCST_DIR) && $(MAKE) $@ @@ -183,6 +193,7 @@ clean: @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi + @if [ -d $(EMULEX_DIR) ]; then cd $(EMULEX_DIR) && $(MAKE) $@; fi extraclean: -rm -f TAGS @@ -197,6 +208,7 @@ extraclean: @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi + @if [ -d $(EMULEX_DIR) ]; then cd $(EMULEX_DIR) && $(MAKE) $@; fi tags: find . -type f -name "*.[ch]" | ctags --c-kinds=+p --fields=+iaS --extra=+q -e -L- @@ -257,7 +269,7 @@ qla_clean: cd $(QLA_DIR) && $(MAKE) clean qla_extraclean: - cd $(QLA_INI_DIR)/.. && $(MAKE) extraclean + cd $(QLA_INI_DIR) && pwd && $(MAKE) extraclean cd $(QLA_DIR) && $(MAKE) extraclean qla_old: @@ -290,6 +302,21 @@ iscsi_clean: iscsi_extraclean: cd $(ISCSI_DIR) && $(MAKE) extraclean +emulex: + cd $(EMULEX_DIR) && $(MAKE) all + +emulex_install: + cd $(EMULEX_DIR) && $(MAKE) install + +emulex_uninstall: + cd $(EMULEX_DIR) && $(MAKE) uninstall + +emulex_clean: + cd $(EMULEX_DIR) && $(MAKE) clean + +emulex_extraclean: + cd $(EMULEX_DIR) && $(MAKE) extraclean + lsi: cd $(LSI_DIR) && $(MAKE) all @@ -405,9 +432,25 @@ scst-rpm: for d in BUILD RPMS SOURCES SPECS SRPMS; do \ mkdir -p $${rpmtopdir}/$$d; \ done && \ - cp $${name}-$(VERSION).tar.bz2 $${rpmtopdir}/SOURCES && \ + cp scst-$(VERSION).tar.bz2 $${rpmtopdir}/SOURCES && \ sed "s/@rpm_version@/$(VERSION)/g" \ - <$${name}.spec.in >$${name}.spec; \ + <$${name}.spec.in >$${name}.spec && \ + MAKE="$(MAKE)" rpmbuild --define="%_topdir $${rpmtopdir}" \ + $(if $(KVER),--define="%kversion $(KVER)") \ + -ba $${name}.spec && \ + rm -f $${name}-$(VERSION).tar.bz2 + +scst-dkms-rpm: + name=scst-dkms && \ + rpmtopdir="$$(if [ $$(id -u) = 0 ]; then echo /usr/src/packages;\ + else echo $$PWD/rpmbuilddir; fi)" && \ + $(MAKE) scst-dist-gzip && \ + for d in BUILD RPMS SOURCES SPECS SRPMS; do \ + mkdir -p $${rpmtopdir}/$$d; \ + done && \ + cp scst-$(VERSION).tar.bz2 $${rpmtopdir}/SOURCES && \ + sed "s/@rpm_version@/$(VERSION)/g" \ + <$${name}.spec.in >$${name}.spec && \ MAKE="$(MAKE)" rpmbuild --define="%_topdir $${rpmtopdir}" \ $(if $(KVER),--define="%kversion $(KVER)") \ -ba $${name}.spec && \ @@ -422,6 +465,16 @@ rpm: find -name '*.rpm'; \ fi +release-archive: + $(MAKE) 2release + for m in $$(find -name Makefile | \ + xargs grep -l '^release-archive:' | \ + grep -v '^\./Makefile'); \ + do \ + (cd $$(dirname $$m) && $(MAKE) release-archive) \ + done + $(MAKE) 2debug + 2perf: extraclean cd $(SCST_DIR) && $(MAKE) $@ @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi @@ -480,6 +533,7 @@ disable_proc: extraclean qla_old qla_old_install qla_old_uninstall qla_old_clean qla_old_extraclean \ lsi lsi_install lsi_uninstall lsi_clean lsi_extraclean \ iscsi iscsi_install iscsi_uninstall iscsi_clean iscsi_extraclean \ + emulex emulex_install emulex_uninstall emulex_clean emulex_extraclean \ scst scst_install scst_uninstall scst_clean scst_extraclean \ docs docs_clean docs_extraclean \ scstadm scstadm_install scstadm_uninstall scstadm_clean scstadm_extraclean \ diff --git a/SVN_TAGS b/SVN_TAGS index 954f3772c..789a05d4c 100644 --- a/SVN_TAGS +++ b/SVN_TAGS @@ -17,3 +17,5 @@ SRPT 1.0.0 450 2.2.x branch start 3981, which is a copy of r3979 on the 2.1.0.x branch 2.2.0 4102 2.2.1 4737 on the 2.2.x branch +3.0.x branch start 5534, which is a copy of trunk r5533. +3.0.0 5815 on the 3.0.x branch diff --git a/doc/scst_user_spec.sgml b/doc/scst_user_spec.sgml index 467f9e8e1..08ec6efb9 100644 --- a/doc/scst_user_spec.sgml +++ b/doc/scst_user_spec.sgml @@ -151,16 +151,8 @@ function, or close on its file descriptor. SCST_USER_UNREGISTER_DEVICE

-SCST_USER_UNREGISTER_DEVICE unregisters the corresponding virtual user -space device. It doesn't have any parameters. - -During execution of SCST_USER_UNREGISTER_DEVICE at least one another -thread must process all coming subcommands, otherwise after timeout it -will fail with EBUSY error. The processing should stop on receiving -ENODEV error. - -SCST_USER_UNREGISTER_DEVICE returns 0 on success or -1 in case of error, -and errno is set appropriately. +SCST_USER_UNREGISTER_DEVICE is obsolete and should not be used. Just +close the device's fd instead. SCST_USER_SET_OPTIONS/SCST_USER_GET_OPTIONS @@ -290,6 +282,10 @@ thread, which also handles incoming commands, otherwise there could be a but nobody handles them. This "deadlock" will be resolved only when initiator, which sent those commands, aborts them after timeout. + SCST_USER_REPLY_AND_GET_CMD diff --git a/fcst/Makefile b/fcst/Makefile index d70f7626c..6ed535a59 100644 --- a/fcst/Makefile +++ b/fcst/Makefile @@ -3,7 +3,7 @@ # Based on ../mvsas_tgt/Makefile # # Copyright (C) 2006 - 2008 Jacky Feng -# Copyright (C) 2011 - 2014 Bart Van Assche +# Copyright (C) 2011 - 2015 Bart Van Assche # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/fcst/fcst.h b/fcst/fcst.h index 6067b84bd..9f2276b01 100644 --- a/fcst/fcst.h +++ b/fcst/fcst.h @@ -147,7 +147,6 @@ int ft_send_xfer_rdy(struct scst_cmd *); void ft_cmd_timeout(struct scst_cmd *); void ft_cmd_free(struct scst_cmd *); void ft_cmd_tm_done(struct scst_mgmt_cmd *); -int ft_tgt_detect(struct scst_tgt_template *); int ft_tgt_release(struct scst_tgt *); int ft_tgt_enable(struct scst_tgt *, bool); bool ft_tgt_enabled(struct scst_tgt *); diff --git a/fcst/ft_cmd.c b/fcst/ft_cmd.c index 8c3509a78..3900e425d 100644 --- a/fcst/ft_cmd.c +++ b/fcst/ft_cmd.c @@ -57,7 +57,8 @@ void ft_cmd_dump(struct scst_cmd *cmd, const char *caller) prefix, cmd, fcmd->write_data_len, fcmd->read_data_len); pr_info("%s exp_dir %x exp_xfer_len %d exp_in_len %d\n", prefix, cmd->expected_data_direction, - cmd->expected_transfer_len, cmd->expected_out_transfer_len); + cmd->expected_transfer_len_full, + cmd->expected_out_transfer_len); pr_info("%s dir %x data_len %lld bufflen %d out_bufflen %d\n", prefix, cmd->data_direction, cmd->data_len, cmd->bufflen, cmd->out_bufflen); diff --git a/fcst/ft_scst.c b/fcst/ft_scst.c index fb4e576f1..96e44a724 100644 --- a/fcst/ft_scst.c +++ b/fcst/ft_scst.c @@ -44,7 +44,6 @@ struct scst_tgt_template ft_scst_template = { .on_hw_pending_cmd_timeout = ft_cmd_timeout, .on_free_cmd = ft_cmd_free, .task_mgmt_fn_done = ft_cmd_tm_done, - .detect = ft_tgt_detect, .release = ft_tgt_release, .report_aen = ft_report_aen, .enable_target = ft_tgt_enable, diff --git a/fcst/ft_sess.c b/fcst/ft_sess.c index 81caf8bae..f98973408 100644 --- a/fcst/ft_sess.c +++ b/fcst/ft_sess.c @@ -26,8 +26,6 @@ static void ft_sess_put(struct ft_sess *sess); -static int ft_tport_count; - static ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn) { u8 b[8]; @@ -75,7 +73,6 @@ static struct ft_tport *ft_tport_create(struct fc_lport *lport) return NULL; } scst_tgt_set_tgt_priv(tport->tgt, tport); - ft_tport_count++; tport->lport = lport; for (i = 0; i < FT_SESS_HASH_SIZE; i++) @@ -120,7 +117,6 @@ static void ft_tport_delete(struct ft_tport *tport) #else call_rcu(&tport->rcu, ft_tport_rcu_free); #endif - ft_tport_count--; } /* @@ -643,11 +639,6 @@ bool ft_tgt_enabled(struct scst_tgt *tgt) return tport->enabled; } -int ft_tgt_detect(struct scst_tgt_template *tt) -{ - return ft_tport_count; -} - /* * Report AEN (Asynchronous Event Notification) from device to initiator. * See notes in scst.h. diff --git a/ibmvstgt/src/ibmvstgt.c b/ibmvstgt/src/ibmvstgt.c index 1dd4dbc7e..5aa033ddc 100644 --- a/ibmvstgt/src/ibmvstgt.c +++ b/ibmvstgt/src/ibmvstgt.c @@ -116,7 +116,6 @@ struct vio_port { bool enabled; }; -static atomic_t ibmvstgt_device_count; static struct workqueue_struct *vtgtd; static unsigned max_vdma_size = MAX_H_COPY_RDMA; static struct scst_tgt_template ibmvstgt_template; @@ -392,16 +391,6 @@ static bool ibmvstgt_is_target_enabled(struct scst_tgt *scst_tgt) } #endif -/** - * ibmvstgt_detect() - Returns the number of target adapters. - * - * Callback function called by the SCST core. - */ -static int ibmvstgt_detect(struct scst_tgt_template *tp) -{ - return atomic_read(&ibmvstgt_device_count); -} - /** * ibmvstgt_release() - Free the resources associated with an SCST target. * @@ -1225,7 +1214,6 @@ static struct scst_tgt_template ibmvstgt_template = { .enable_target = ibmvstgt_enable_target, .is_target_enabled = ibmvstgt_is_target_enabled, #endif - .detect = ibmvstgt_detect, .release = ibmvstgt_release, .xmit_response = ibmvstgt_xmit_response, .rdy_to_xfer = ibmvstgt_rdy_to_xfer, @@ -1308,8 +1296,6 @@ static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id) #endif goto destroy_crq_queue; - atomic_inc(&ibmvstgt_device_count); - return 0; destroy_crq_queue: @@ -1334,8 +1320,6 @@ static int ibmvstgt_remove(struct vio_dev *dev) if (!target) return 0; - atomic_dec(&ibmvstgt_device_count); - vport = target->ldata; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) class_device_unregister(&vport->dev); diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index e04eeccca..b9f16e358 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -111,8 +111,8 @@ include/iscsi_scst_itf_ver.h: include/iscsi_scst.h echo "/* Autogenerated, don't edit */" >include/iscsi_scst_itf_ver.h echo "" >>include/iscsi_scst_itf_ver.h echo -n "#define ISCSI_SCST_INTERFACE_VERSION " >>include/iscsi_scst_itf_ver.h - echo -n "ISCSI_VERSION_STRING \"_\" " >>include/iscsi_scst_itf_ver.h - echo "\"`sha1sum include/iscsi_scst.h|awk '{printf $$1}'`\"" >>include/iscsi_scst_itf_ver.h + echo -n "ISCSI_VERSION_STRING \"_" >>include/iscsi_scst_itf_ver.h + echo "`sha1sum include/iscsi_scst.h|awk '{printf $$1}'`\"" >>include/iscsi_scst_itf_ver.h install: all @install -vD -m 755 usr/iscsi-scstd $(DESTDIR)$(SBINDIR)/iscsi-scstd diff --git a/iscsi-scst/Makefile_user_space_only b/iscsi-scst/Makefile_user_space_only index 695ac9fa3..24032afb7 100644 --- a/iscsi-scst/Makefile_user_space_only +++ b/iscsi-scst/Makefile_user_space_only @@ -19,8 +19,8 @@ include/iscsi_scst_itf_ver.h: include/iscsi_scst.h echo "/* Autogenerated, don't edit */" >include/iscsi_scst_itf_ver.h echo "" >>include/iscsi_scst_itf_ver.h echo -n "#define ISCSI_SCST_INTERFACE_VERSION " >>include/iscsi_scst_itf_ver.h - echo -n "ISCSI_VERSION_STRING \"_\" " >>include/iscsi_scst_itf_ver.h - echo "\"`sha1sum include/iscsi_scst.h|awk '{printf $$1}'`\"" >>include/iscsi_scst_itf_ver.h + echo -n "ISCSI_VERSION_STRING \"_" >>include/iscsi_scst_itf_ver.h + echo "`sha1sum include/iscsi_scst.h|awk '{printf $$1}'`\"" >>include/iscsi_scst_itf_ver.h install: all @install -vD -m 755 usr/iscsi-scstd $(DESTDIR)$(SBINDIR)/iscsi-scstd diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index a79cf56d8..f64d50096 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/include/iscsi_scst_ver.h b/iscsi-scst/include/iscsi_scst_ver.h index 8e3790195..566425c85 100644 --- a/iscsi-scst/include/iscsi_scst_ver.h +++ b/iscsi-scst/include/iscsi_scst_ver.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/Makefile b/iscsi-scst/kernel/Makefile index cfe3e4928..39c837a2b 100644 --- a/iscsi-scst/kernel/Makefile +++ b/iscsi-scst/kernel/Makefile @@ -1,8 +1,8 @@ # # Makefile for the kernel part of iSCSI-SCST. # -# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2007 - 2015 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index 3ae4e0567..8916ce211 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1270,18 +1270,18 @@ void iscsi_dump_pdu(struct iscsi_pdu *pdu) int i; buf = (void *)&pdu->bhs; - printk(KERN_DEBUG "BHS: (%p,%zd)\n", buf, sizeof(pdu->bhs)); + pr_debug("BHS: (%p,%zd)\n", buf, sizeof(pdu->bhs)); for (i = 0; i < (int)sizeof(pdu->bhs); i++) iscsi_dump_char(*buf++, text, &pos); iscsi_dump_char(-1, text, &pos); buf = (void *)pdu->ahs; - printk(KERN_DEBUG "AHS: (%p,%d)\n", buf, pdu->ahssize); + pr_debug("AHS: (%p,%d)\n", buf, pdu->ahssize); for (i = 0; i < pdu->ahssize; i++) iscsi_dump_char(*buf++, text, &pos); iscsi_dump_char(-1, text, &pos); - printk(KERN_DEBUG "Data: (%d)\n", pdu->datasize); + pr_debug("Data: (%d)\n", pdu->datasize); } } diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index b35e03c30..309e2d4ac 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -766,7 +766,7 @@ static int conn_setup_sock(struct iscsi_conn *conn) TRACE_DBG("%llx", (unsigned long long int)session->sid); - conn->sock = SOCKET_I(conn->file->f_dentry->d_inode); + conn->sock = SOCKET_I(file_inode(conn->file)); if (conn->sock->ops->sendpage == NULL) { PRINT_ERROR("Socket for sid %llx doesn't support sendpage()", diff --git a/iscsi-scst/kernel/digest.c b/iscsi-scst/kernel/digest.c index e9c7c7787..1da1a04d0 100644 --- a/iscsi-scst/kernel/digest.c +++ b/iscsi-scst/kernel/digest.c @@ -3,8 +3,8 @@ * * Copyright (C) 2004 - 2006 Xiranet Communications GmbH * - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/digest.h b/iscsi-scst/kernel/digest.h index 3a72b25f6..d13289d21 100644 --- a/iscsi-scst/kernel/digest.h +++ b/iscsi-scst/kernel/digest.h @@ -2,8 +2,8 @@ * iSCSI digest handling. * * Copyright (C) 2004 Xiranet Communications GmbH - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/event.c b/iscsi-scst/kernel/event.c index ffd9e13b5..568f24108 100644 --- a/iscsi-scst/kernel/event.c +++ b/iscsi-scst/kernel/event.c @@ -2,8 +2,8 @@ * Event notification code. * * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 4a8115526..cdd7c8314 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1450,8 +1450,8 @@ static void cmnd_prepare_get_rejected_immed_data(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; struct scatterlist *sg = cmnd->sg; - char __user *addr; - u32 size; + char *addr; + u32 size, s, e; unsigned int i; TRACE_ENTRY(); @@ -1484,18 +1484,22 @@ static void cmnd_prepare_get_rejected_immed_data(struct iscsi_cmnd *cmnd) cmnd->own_sg = 1; } - addr = (char __force __user *)(page_address(sg_page(&sg[0]))); - conn->read_size = size; - for (i = 0; size > PAGE_SIZE; i++, size -= PAGE_SIZE) { + addr = page_address(sg_page(&sg[0])); + for (s = size, i = 0; s > 0; i++, s -= e) { /* We already checked pdu.datasize in check_segment_length() */ sBUG_ON(i >= ISCSI_CONN_IOV_MAX); conn->read_iov[i].iov_base = addr; - conn->read_iov[i].iov_len = PAGE_SIZE; + e = min_t(u32, s, PAGE_SIZE); + conn->read_iov[i].iov_len = e; } - conn->read_iov[i].iov_base = addr; - conn->read_iov[i].iov_len = size; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + iov_iter_kvec(&conn->read_msg.msg_iter, READ | ITER_KVEC, + conn->read_iov, i, size); +#else conn->read_msg.msg_iov = conn->read_iov; - conn->read_msg.msg_iovlen = ++i; + conn->read_msg.msg_iovlen = i; + conn->read_size = size; +#endif out: TRACE_EXIT(); @@ -1576,6 +1580,7 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, struct scatterlist *sg = cmd->sg; unsigned int bufflen = cmd->bufflen; unsigned int idx, i, buff_offs; + const u32 read_size = size; int res = 0; TRACE_ENTRY(); @@ -1590,13 +1595,10 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, idx = offset >> PAGE_SHIFT; offset &= ~PAGE_MASK; - conn->read_msg.msg_iov = conn->read_iov; - conn->read_size = size; - i = 0; while (1) { unsigned int sg_len; - char __user *addr; + char *addr; if (unlikely(buff_offs >= bufflen)) { TRACE_DBG("Residual overflow (cmd %p, buff_offs %d, " @@ -1606,7 +1608,7 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, offset = 0; } - addr = (char __force __user *)(page_address(sg_page(&sg[idx]))); + addr = page_address(sg_page(&sg[idx])); EXTRACHECKS_BUG_ON(addr == NULL); sg_len = sg[idx].offset + sg[idx].length - offset; @@ -1616,7 +1618,6 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, TRACE_DBG("idx=%d, i=%d, offset=%u, size=%d, addr=%p", idx, i, offset, size, addr); conn->read_iov[i].iov_len = size; - conn->read_msg.msg_iovlen = i+1; break; } conn->read_iov[i].iov_len = sg_len; @@ -1635,16 +1636,26 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, size); mark_conn_closed(conn); res = -EINVAL; - break; + goto out; } idx++; offset = 0; } - TRACE_DBG("msg_iov=%p, msg_iovlen=%zd", - conn->read_msg.msg_iov, conn->read_msg.msg_iovlen); + i++; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + iov_iter_kvec(&conn->read_msg.msg_iter, READ | ITER_KVEC, + conn->read_iov, i, read_size); +#else + conn->read_msg.msg_iov = conn->read_iov; + conn->read_msg.msg_iovlen = i; + conn->read_size = read_size; +#endif + TRACE_DBG("msg_iov=%p, msg_iovlen=%u", conn->read_iov, i); + +out: TRACE_EXIT_RES(res); return res; } @@ -1784,7 +1795,6 @@ static int nop_out_start(struct iscsi_cmnd *cmnd) size = cmnd->pdu.datasize; if (size && !conn->session->sess_params.rdma_extensions) { - conn->read_msg.msg_iov = conn->read_iov; if (cmnd->pdu.bhs.itt != ISCSI_RESERVED_TAG) { struct scatterlist *sg; @@ -1805,10 +1815,9 @@ static int nop_out_start(struct iscsi_cmnd *cmnd) for (i = 0; i < cmnd->sg_cnt; i++) { conn->read_iov[i].iov_base = - (void __force __user *)(page_address(sg_page(&sg[i]))); + page_address(sg_page(&sg[i])); tmp = min_t(u32, size, PAGE_SIZE); conn->read_iov[i].iov_len = tmp; - conn->read_size += tmp; size -= tmp; } sBUG_ON(size != 0); @@ -1820,10 +1829,9 @@ static int nop_out_start(struct iscsi_cmnd *cmnd) */ for (i = 0; i < (signed)ISCSI_CONN_IOV_MAX; i++) { conn->read_iov[i].iov_base = - (void __force __user *)(page_address(dummy_page)); + page_address(dummy_page); tmp = min_t(u32, size, PAGE_SIZE); conn->read_iov[i].iov_len = tmp; - conn->read_size += tmp; size -= tmp; } @@ -1831,9 +1839,15 @@ static int nop_out_start(struct iscsi_cmnd *cmnd) sBUG_ON(size != 0); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + iov_iter_kvec(&conn->read_msg.msg_iter, READ | ITER_KVEC, + conn->read_iov, i, cmnd->pdu.datasize); +#else + conn->read_msg.msg_iov = conn->read_iov; conn->read_msg.msg_iovlen = i; - TRACE_DBG("msg_iov=%p, msg_iovlen=%zd", conn->read_msg.msg_iov, - conn->read_msg.msg_iovlen); + conn->read_size = cmnd->pdu.datasize; +#endif + TRACE_DBG("msg_iov=%p, msg_iovlen=%d", conn->read_iov, i); } out: @@ -3995,12 +4009,6 @@ out: } #endif -static int iscsi_target_detect(struct scst_tgt_template *templ) -{ - /* Nothing to do */ - return 0; -} - static int iscsi_target_release(struct scst_tgt *scst_tgt) { /* Nothing to do */ @@ -4055,7 +4063,6 @@ struct scst_tgt_template iscsi_template = { .trace_tbl_help = ISCSI_TRACE_TBL_HELP, #endif #endif - .detect = iscsi_target_detect, .release = iscsi_target_release, .xmit_response = iscsi_xmit_response, #if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index c89880f85..84aefeb23 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -291,9 +291,13 @@ struct iscsi_conn { */ struct iscsi_cmnd *read_cmnd; struct msghdr read_msg; - u32 read_size; int read_state; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + struct kvec *read_iov; +#else + u32 read_size; struct iovec *read_iov; +#endif struct task_struct *rx_task; uint32_t rpadding; diff --git a/iscsi-scst/kernel/iscsi_dbg.h b/iscsi-scst/kernel/iscsi_dbg.h index 170a23217..6f6e247b8 100644 --- a/iscsi-scst/kernel/iscsi_dbg.h +++ b/iscsi-scst/kernel/iscsi_dbg.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/iscsi_hdr.h b/iscsi-scst/kernel/iscsi_hdr.h index 6204699c7..67e729697 100644 --- a/iscsi-scst/kernel/iscsi_hdr.h +++ b/iscsi-scst/kernel/iscsi_hdr.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 1da884f36..4dea0cf55 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -2,8 +2,8 @@ * Network threads. * * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -616,13 +616,18 @@ void start_close_conn(struct iscsi_conn *conn) EXPORT_SYMBOL(start_close_conn); static inline void iscsi_conn_init_read(struct iscsi_conn *conn, - void __user *data, size_t len) + void *data, size_t len) { conn->read_iov[0].iov_base = data; conn->read_iov[0].iov_len = len; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + iov_iter_kvec(&conn->read_msg.msg_iter, READ | ITER_KVEC, + conn->read_iov, 1, len); +#else conn->read_msg.msg_iov = conn->read_iov; conn->read_msg.msg_iovlen = 1; conn->read_size = len; +#endif return; } @@ -634,7 +639,7 @@ static void iscsi_conn_prepare_read_ahs(struct iscsi_conn *conn, /* ToDo: __GFP_NOFAIL ?? */ cmnd->pdu.ahs = kmalloc(asize, __GFP_NOFAIL|GFP_KERNEL); sBUG_ON(cmnd->pdu.ahs == NULL); - iscsi_conn_init_read(conn, (void __force __user *)cmnd->pdu.ahs, asize); + iscsi_conn_init_read(conn, cmnd->pdu.ahs, asize); return; } @@ -684,8 +689,12 @@ static int do_recv(struct iscsi_conn *conn) { int res; mm_segment_t oldfs; - struct msghdr msg; + struct msghdr *msg; + int read_size; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + struct iovec *first_iov; int first_len; +#endif EXTRACHECKS_BUG_ON(conn->read_cmnd == NULL); @@ -701,45 +710,48 @@ static int do_recv(struct iscsi_conn *conn) */ restart: - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = conn->read_msg.msg_iov; - msg.msg_iovlen = conn->read_msg.msg_iovlen; - first_len = msg.msg_iov->iov_len; + msg = &conn->read_msg; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + read_size = msg->msg_iter.count; +#else + read_size = conn->read_size; + first_iov = msg->msg_iov; + first_len = first_iov->iov_len; +#endif oldfs = get_fs(); set_fs(get_ds()); - res = sock_recvmsg(conn->sock, &msg, conn->read_size, + res = sock_recvmsg(conn->sock, msg, read_size, MSG_DONTWAIT | MSG_NOSIGNAL); set_fs(oldfs); - TRACE_DBG("msg_iovlen %zd, first_len %d, read_size %d, res %d", - msg.msg_iovlen, first_len, conn->read_size, res); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + TRACE_DBG("nr_segs %zd, bytes_left %zd, res %d", + msg->msg_iter.nr_segs, msg->msg_iter.count, res); +#else + TRACE_DBG("msg_iovlen %zd, read_size %d, res %d", msg->msg_iovlen, + read_size, res); +#endif if (res > 0) { /* - * To save some considerable effort and CPU power we - * suppose that TCP functions adjust - * conn->read_msg.msg_iov and conn->read_msg.msg_iovlen - * on amount of copied data. This BUG_ON is intended - * to catch if it is changed in the future. + * To save CPU cycles we suppose that sock_recvmsg() adjusts + * msg->msg_iov and msg->msg_iovlen. The BUG_ON() statements + * below verifies this. */ - sBUG_ON((res >= first_len) && - (conn->read_msg.msg_iov->iov_len != 0)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + sBUG_ON(msg->msg_iter.count + res != read_size); + res = msg->msg_iter.count; +#else + sBUG_ON((res >= first_len) && (first_iov->iov_len != 0)); conn->read_size -= res; - if (conn->read_size != 0) { - if (res >= first_len) { - int done = 1 + ((res - first_len) >> PAGE_SHIFT); - TRACE_DBG("done %d", done); - conn->read_msg.msg_iov += done; - conn->read_msg.msg_iovlen -= done; - } - } res = conn->read_size; +#endif } else { switch (res) { case -EAGAIN: TRACE_DBG("EAGAIN received for conn %p", conn); - res = conn->read_size; + res = read_size; break; case -ERESTARTSYS: TRACE_DBG("ERESTARTSYS received for conn %p", conn); @@ -825,7 +837,7 @@ static int iscsi_rx_check_ddigest(struct iscsi_conn *conn) static int process_read_io(struct iscsi_conn *conn, int *closed) { struct iscsi_cmnd *cmnd = conn->read_cmnd; - int res; + int bytes_left, res; TRACE_ENTRY(); @@ -837,9 +849,8 @@ static int process_read_io(struct iscsi_conn *conn, int *closed) EXTRACHECKS_BUG_ON(conn->read_cmnd != NULL); cmnd = conn->transport->iscsit_alloc_cmd(conn, NULL); conn->read_cmnd = cmnd; - iscsi_conn_init_read(cmnd->conn, - (void __force __user *)&cmnd->pdu.bhs, - sizeof(cmnd->pdu.bhs)); + iscsi_conn_init_read(cmnd->conn, &cmnd->pdu.bhs, + sizeof(cmnd->pdu.bhs)); conn->read_state = RX_BHS; /* go through */ @@ -904,7 +915,7 @@ static int process_read_io(struct iscsi_conn *conn, int *closed) if (psz != 0) { TRACE_DBG("padding %d bytes", psz); iscsi_conn_init_read(conn, - (void __force __user *)&conn->rpadding, psz); + &conn->rpadding, psz); conn->read_state = RX_PADDING; } else if ((conn->ddigest_type & DIGEST_NONE) != 0) conn->read_state = RX_END; @@ -914,10 +925,15 @@ static int process_read_io(struct iscsi_conn *conn, int *closed) break; case RX_END: - if (unlikely(conn->read_size != 0)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + bytes_left = conn->read_msg.msg_iter.count; +#else + bytes_left = conn->read_size; +#endif + if (unlikely(bytes_left != 0)) { PRINT_CRIT_ERROR("conn read_size !=0 on RX_END " "(conn %p, op %x, read_size %d)", conn, - cmnd_opcode(cmnd), conn->read_size); + cmnd_opcode(cmnd), bytes_left); sBUG(); } conn->read_cmnd = NULL; @@ -925,7 +941,11 @@ static int process_read_io(struct iscsi_conn *conn, int *closed) cmnd_rx_end(cmnd); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + EXTRACHECKS_BUG_ON(conn->read_msg.msg_iter.count != 0); +#else EXTRACHECKS_BUG_ON(conn->read_size != 0); +#endif /* * To maintain fairness. Res must be 0 here anyway, the @@ -936,8 +956,7 @@ static int process_read_io(struct iscsi_conn *conn, int *closed) goto out; case RX_INIT_HDIGEST: - iscsi_conn_init_read(conn, - (void __force __user *)&cmnd->hdigest, sizeof(u32)); + iscsi_conn_init_read(conn, &cmnd->hdigest, sizeof(u32)); conn->read_state = RX_CHECK_HDIGEST; /* go through */ @@ -957,9 +976,7 @@ static int process_read_io(struct iscsi_conn *conn, int *closed) break; case RX_INIT_DDIGEST: - iscsi_conn_init_read(conn, - (void __force __user *)&cmnd->ddigest, - sizeof(u32)); + iscsi_conn_init_read(conn, &cmnd->ddigest, sizeof(u32)); conn->read_state = RX_CHECK_DDIGEST; /* go through */ diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c index 702d29ca3..525cd633a 100644 --- a/iscsi-scst/kernel/param.c +++ b/iscsi-scst/kernel/param.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.19.patch b/iscsi-scst/kernel/patches/put_page_callback-3.19.patch new file mode 100644 index 000000000..406350c0d --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.19.patch @@ -0,0 +1,387 @@ +Subject: [PATCH] put_page_callback + +--- + drivers/block/drbd/drbd_receiver.c | 2 +- + include/linux/mm_types.h | 11 +++++++++ + include/linux/net.h | 40 ++++++++++++++++++++++++++++++ + include/linux/skbuff.h | 4 +-- + net/Kconfig | 12 +++++++++ + net/ceph/pagevec.c | 2 +- + net/core/skbuff.c | 14 +++++------ + net/core/sock.c | 4 +-- + net/ipv4/Makefile | 1 + + net/ipv4/ip_output.c | 4 +-- + net/ipv4/tcp.c | 4 +-- + net/ipv4/tcp_zero_copy.c | 50 ++++++++++++++++++++++++++++++++++++++ + net/ipv6/ip6_output.c | 2 +- + 13 files changed, 132 insertions(+), 18 deletions(-) + create mode 100644 net/ipv4/tcp_zero_copy.c + +diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c +index d169b4a..ec913c2 100644 +--- a/drivers/block/drbd/drbd_receiver.c ++++ b/drivers/block/drbd/drbd_receiver.c +@@ -132,7 +132,7 @@ static int page_chain_free(struct page *page) + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 6d34aa2..a536ed7 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -197,6 +197,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops +diff --git a/include/linux/net.h b/include/linux/net.h +index 17d8339..f784384 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -285,6 +286,45 @@ int kernel_sendpage(struct socket *sock, struct page *page, int offset, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index 85ab7d7..fb82e86 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2254,7 +2254,7 @@ static inline struct page *skb_frag_page(const skb_frag_t *frag) + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -2277,7 +2277,7 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f) + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** +diff --git a/net/Kconfig b/net/Kconfig +index ff9ffc1..a270579 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -76,6 +76,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" +diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c +index 5550130..993f710 100644 +--- a/net/ceph/pagevec.c ++++ b/net/ceph/pagevec.c +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty) + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + if (is_vmalloc_addr(pages)) + vfree(pages); +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 62c67be..35074d3 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -479,7 +479,7 @@ static struct sk_buff *__alloc_rx_skb(unsigned int length, gfp_t gfp_mask, + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length, gfp_mask, +@@ -592,7 +592,7 @@ static void skb_clone_fraglist(struct sk_buff *skb) + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -920,7 +920,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1767,7 +1767,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1820,7 +1820,7 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2279,7 +2279,7 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } +@@ -2933,7 +2933,7 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); +diff --git a/net/core/sock.c b/net/core/sock.c +index 1c7a33d..ec95185 100644 +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -1864,7 +1864,7 @@ bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t gfp) + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + pfrag->offset = 0; +@@ -2615,7 +2615,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index 518c04e..4072a87 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -57,6 +57,7 @@ obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o + obj-$(CONFIG_GENEVE) += geneve.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o xfrm4_protocol.o +diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +index 2e2f687..87f5bff 100644 +--- a/net/ipv4/ip_output.c ++++ b/net/ipv4/ip_output.c +@@ -1052,7 +1052,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1277,7 +1277,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 3075723..396b887 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -931,7 +931,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1232,7 +1232,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } +diff --git a/net/ipv4/tcp_zero_copy.c b/net/ipv4/tcp_zero_copy.c +new file mode 100644 +index 0000000..430147e +--- /dev/null ++++ b/net/ipv4/tcp_zero_copy.c +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 3f5aa99..777f267 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -1460,7 +1460,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +-- +1.9.1 + diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index 121389aab..d892e5907 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index 0c3280d3f..b12fa6889 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/Makefile b/iscsi-scst/usr/Makefile index ae70b9a81..f9953a253 100644 --- a/iscsi-scst/usr/Makefile +++ b/iscsi-scst/usr/Makefile @@ -1,8 +1,8 @@ # # Makefile for the user space part of iSCSI-SCST. # -# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2007 - 2015 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,9 +26,6 @@ OBJS_D = $(SRCS_D:.c=.o) SRCS_ADM = iscsi_adm.c param.c OBJS_ADM = $(SRCS_ADM:.c=.o) -SCST_INC_DIR := ../../scst/include -#SCST_INC_DIR := $(PREFIX)/include/scst - CFLAGS += -O2 -fno-inline -Wall -Wextra -Wstrict-prototypes -Wno-sign-compare \ -Wimplicit-function-declaration -Wno-unused-parameter \ -Wno-missing-field-initializers -g -I../include -I$(SCST_INC_DIR) diff --git a/iscsi-scst/usr/chap.c b/iscsi-scst/usr/chap.c index a54309be4..febf69153 100644 --- a/iscsi-scst/usr/chap.c +++ b/iscsi-scst/usr/chap.c @@ -3,8 +3,8 @@ * * Copyright (C) 2004 Xiranet Communications GmbH * Copyright (C) 2002 - 2003 Ardis Technolgies , - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * and code taken from UNH iSCSI software: * Copyright (C) 2001-2003 InterOperability Lab (IOL) diff --git a/iscsi-scst/usr/config.c b/iscsi-scst/usr/config.c index 0ea14491a..f6017e5e7 100644 --- a/iscsi-scst/usr/config.c +++ b/iscsi-scst/usr/config.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/conn.c b/iscsi-scst/usr/conn.c index 8d4391762..4d62c3eff 100644 --- a/iscsi-scst/usr/conn.c +++ b/iscsi-scst/usr/conn.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index 1164fc7d4..f83668675 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/event.c b/iscsi-scst/usr/event.c index c13865839..719c7e2a6 100644 --- a/iscsi-scst/usr/event.c +++ b/iscsi-scst/usr/event.c @@ -2,8 +2,8 @@ * Event notification code. * * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_adm.c b/iscsi-scst/usr/iscsi_adm.c index 57242f044..dc0a85f2e 100644 --- a/iscsi-scst/usr/iscsi_adm.c +++ b/iscsi-scst/usr/iscsi_adm.c @@ -2,8 +2,8 @@ * iscsi_adm - manage iSCSI-SCST Target software. * * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_adm.h b/iscsi-scst/usr/iscsi_adm.h index 4408368e4..ba87ff10f 100644 --- a/iscsi-scst/usr/iscsi_adm.h +++ b/iscsi-scst/usr/iscsi_adm.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_hdr.h b/iscsi-scst/usr/iscsi_hdr.h index 95b8b2280..c766be279 100644 --- a/iscsi-scst/usr/iscsi_hdr.h +++ b/iscsi-scst/usr/iscsi_hdr.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 69a67bb07..322b4c321 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index 2e104a756..1e4ee87bc 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 456b72a25..a245b3513 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/isns.c b/iscsi-scst/usr/isns.c index cdd7c3e4e..c5d5eafdf 100644 --- a/iscsi-scst/usr/isns.c +++ b/iscsi-scst/usr/isns.c @@ -2,8 +2,8 @@ * iSNS functions * * Copyright (C) 2006 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/iscsi-scst/usr/isns_proto.h b/iscsi-scst/usr/isns_proto.h index 49e45556f..5bdfd3510 100644 --- a/iscsi-scst/usr/isns_proto.h +++ b/iscsi-scst/usr/isns_proto.h @@ -2,8 +2,8 @@ * iSNS protocol data types * * Copyright (C) 2006 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/iscsi-scst/usr/log.c b/iscsi-scst/usr/log.c index 273cbc330..2a552e290 100644 --- a/iscsi-scst/usr/log.c +++ b/iscsi-scst/usr/log.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/message.c b/iscsi-scst/usr/message.c index 89e0610de..acd257153 100644 --- a/iscsi-scst/usr/message.c +++ b/iscsi-scst/usr/message.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/misc.c b/iscsi-scst/usr/misc.c index 92c5d96c7..357be4d11 100644 --- a/iscsi-scst/usr/misc.c +++ b/iscsi-scst/usr/misc.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/misc.h b/iscsi-scst/usr/misc.h index 37000efb2..879a79ca7 100644 --- a/iscsi-scst/usr/misc.h +++ b/iscsi-scst/usr/misc.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index 8a3f1cc8a..09a93e3cc 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/param.h b/iscsi-scst/usr/param.h index 670b5af63..7b46900aa 100644 --- a/iscsi-scst/usr/param.h +++ b/iscsi-scst/usr/param.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/session.c b/iscsi-scst/usr/session.c index 57f897683..fe25b1c05 100644 --- a/iscsi-scst/usr/session.c +++ b/iscsi-scst/usr/session.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index 952201f30..030d9afa2 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -127,6 +127,15 @@ static int wildcmp(const char *wild, const char *string) return __wildcmp(wild, string, 0); } +/* + * Evaluate the list with wildcard patterns as follows: + * - If only positive wildcard patterns have been specified, it is sufficient + * that one wildcard pattern matches (logical or). + * - If only negative wildcard patterns have been specified, none of these + * patterns must match (logical and). + * - If positive and negative wildcard patterns have been specified, one of + * the positive patterns must match and none of the negative. + */ int target_portal_allowed(struct target *target, const char *target_portal, const char *initiator_name) { @@ -135,14 +144,19 @@ int target_portal_allowed(struct target *target, if (!list_empty(&target->allowed_portals)) { struct iscsi_attr *attr; + bool any_pos_cond = false, pos_match = false, neg_match = true; - res = 0; list_for_each_entry(attr, &target->allowed_portals, ulist) { - if (wildcmp(attr->attr_key, target_portal)) { - res = 1; - break; + bool match = wildcmp(attr->attr_key, target_portal); + + if (attr->attr_key[0] != '!') { + any_pos_cond = true; + pos_match = pos_match || match; + } else { + neg_match = neg_match && match; } } + res = (!any_pos_cond || pos_match) && neg_match; if (res == 0) goto out; } diff --git a/iscsi-scst/usr/types.h b/iscsi-scst/usr/types.h index 147b108fb..a4ff61f85 100644 --- a/iscsi-scst/usr/types.h +++ b/iscsi-scst/usr/types.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index 2fbcbc4b6..a7d386a6a 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,15 +3,15 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.18.5 \ +3.18.8 \ 3.17.8-nc \ 3.16.7-nc \ 3.15.10-nc \ -3.14.31-nc \ +3.14.37-nc \ 3.13.11-nc \ -3.12.36-nc \ +3.12.38-nc \ 3.11.10-nc \ -3.10.67-nc \ +3.10.73-nc \ 3.9.11-nc \ 3.8.13-nc \ 3.7.10-nc \ @@ -19,7 +19,7 @@ ABT_KERNELS=" \ 3.5.7-nc \ 3.4.105-nc \ 3.3.8-nc \ -3.2.66-nc \ +3.2.67-nc \ 3.1.10-nc \ 3.0.101-nc \ 2.6.39.4-nc \ diff --git a/qla2x00t/Makefile b/qla2x00t/Makefile index b1651b067..838e275a7 100644 --- a/qla2x00t/Makefile +++ b/qla2x00t/Makefile @@ -1,7 +1,7 @@ ifeq ($(BUILD_2X_MODULE),) qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \ - qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o + qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o qla_nx.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o @@ -53,7 +53,7 @@ INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra ifneq ($(PATCHLEVEL),) obj-m := qla2xxx_scst.o qla2xxx_scst-objs := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \ - qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o + qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o qla_nx.o else all: diff --git a/qla2x00t/qla2x00-target/ChangeLog b/qla2x00t/qla2x00-target/ChangeLog index 90bc489f9..5fa878e2c 100644 --- a/qla2x00t/qla2x00-target/ChangeLog +++ b/qla2x00t/qla2x00-target/ChangeLog @@ -1,7 +1,15 @@ +Summary of changes between versions 3.0 and 3.1 +----------------------------------------------- + + - Support for 26xx/8[1-3]xx added + + - Update to a newer qla2xxx + + Summary of changes between versions 2.1.0 and 3.0 ------------------------------------------------- - - Update to kernels up to 3.3 + - Update to kernels up to 3.6 - Bug fixes and other improvements diff --git a/qla2x00t/qla2x00-target/Makefile b/qla2x00t/qla2x00-target/Makefile index 67aacf8a1..e3065ff5a 100644 --- a/qla2x00t/qla2x00-target/Makefile +++ b/qla2x00t/qla2x00-target/Makefile @@ -1,9 +1,9 @@ # # Qlogic 2x00 SCSI target driver makefile # -# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/qla2x00t/qla2x00-target/Makefile_in-tree-3.19 b/qla2x00t/qla2x00-target/Makefile_in-tree-3.19 new file mode 100644 index 000000000..9657aee84 --- /dev/null +++ b/qla2x00t/qla2x00-target/Makefile_in-tree-3.19 @@ -0,0 +1,5 @@ +ccflags-y += -Idrivers/scsi/qla2xxx + +qla2x00tgt-y := qla2x00t.o + +obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index 7cf3c3096..ddd612033 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -1,9 +1,17 @@ -Target driver for QLogic 22xx/23xx/24xx/25xx Fibre Channel cards -================================================================ +Target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards +============================================================== -Version 3.1.0, XX XXXXX 2014 +Version 3.1.0, XX XXXXX 2015 ---------------------------- +This is target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards. +Particularly, this driver supports 16G Hilda QLogic chip based adapters +(however, post-Hilda QLogic chips not supported). This driver should +also support FCoE, but that has never been verified. It has passed +intensive internal SanDisk tests. It is stable and production ready. +This driver is in stable maintenance mode in favor of the QLogic git +driver, which has support for all the latest QLogic adapters. + This driver consists from two parts: the target mode driver itself and the changed initiator driver from Linux kernel, which is, particularly, intended to perform all the initialization and shutdown tasks. The @@ -12,22 +20,11 @@ necessary callbacks, but it's still capable to work as initiator only. Mode, when a host acts as the initiator and the target simultaneously, is supported as well. -This version is compatible with SCST core version 2.0.0 and higher and +This version is compatible with SCST core version 3.1.0 and higher and Linux kernel 2.6.26 and higher. Sorry, kernels below 2.6.26 are not supported, because it's too hard to backport used initiator driver to older kernels. -The original initiator driver was taken from the kernel 2.6.26. Also the -following 2.6.26+ commits have been applied to it (upstream ID): -048feec5548c0582ee96148c61b87cccbcb5f9be, -031e134e5f95233d80fb1b62fdaf5e1be587597c, -5f3a9a207f1fccde476dd31b4c63ead2967d934f, -85821c906cf3563a00a3d98fa380a2581a7a5ff1, -3c01b4f9fbb43fc911acd33ea7a14ea7a4f9866b, -8eca3f39c4b11320787f7b216f63214aee8415a9, -0f19bc681ed0849a2b95778460a0a8132e3700e2, -a55aac79de0ea6fc52d35f535867b6573a5ff0f8. - See also "ToDo" file for list of known issues and unimplemented features. diff --git a/qla2x00t/qla2x00-target/ToDo b/qla2x00t/qla2x00-target/ToDo index 939c060af..119374f31 100644 --- a/qla2x00t/qla2x00-target/ToDo +++ b/qla2x00t/qla2x00-target/ToDo @@ -1,10 +1,12 @@ Known issues and unimplemented features --------------------------------------- - - Allow to set port names through sysfs. - + - NPIV targets not quite work. If you need NPIV, use QLogic git driver + - Minor "ToDo"'s spread in the code. +Very old and, probably, already fixed: + - If a Linux initiator asks for devices using INQUIRY command too soon before the controller on the 23xx target is fully initialized in the target mode, the initiator could receive garbage devices and the diff --git a/qla2x00t/qla2x00-target/V2_RELEASE_NOTES b/qla2x00t/qla2x00-target/V2_RELEASE_NOTES deleted file mode 100644 index 72da05896..000000000 --- a/qla2x00t/qla2x00-target/V2_RELEASE_NOTES +++ /dev/null @@ -1,29 +0,0 @@ -Throughout the last three years ID7 and I have been working on an -enterprise class Fibre Channel target based on the QLogic range of 4/8Gb -HBA's. This has been solely funded by ID7 over the last years however -given the positive contributions by many of the SCST community both -personal and commercial, ID7 have chosen to merge with existing QLogic's -open source driver qla2x00t and introduce the driver under GPL license -for community use. - -This driver was designed from ground up to perform at the highest of -levels and interoperate with leading infrastructures, accordingly the -design and flow is much more focused and structured whilst the stability -is as you would expect at the Enterprise level. - -Although there is a current QLogic target driver qla_isp, this driver -has the key advantage that it was designed to be as simple as possible. -As the result, this driver is a lot smaller, cleaner and mainline Linux -kernel ready. It's going to be pushed for the in-kernel inclusion -together other SCST patches. - -Another advantage is that this driver fully supports FC tapes, including -transport level retries on data delivery problems. - -Once again we are grateful for all the positive work and contributions -and hopeful that this target helps drive SCST further forward as an -Enterprise class target. We encourage people to introduce and evaluate -this target driver. - -Vladislav Bolkhovitin, -Mark Klarzynski, ID7 Ltd. (http://www.id-7.com) diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index a66eb3ada..d047c5c7e 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -1,10 +1,10 @@ /* * qla2x00t.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * QLogic 22xx/23xx/24xx/25xx FC target driver. * @@ -30,6 +30,7 @@ #include #include #include +#include #include #ifdef INSIDE_KERNEL_TREE @@ -69,7 +70,6 @@ # endif #endif -static int q2t_target_detect(struct scst_tgt_template *templ); static int q2t_target_release(struct scst_tgt *scst_tgt); static int q2x_xmit_response(struct scst_cmd *scst_cmd); static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type); @@ -80,24 +80,25 @@ static int q2t_get_initiator_port_transport_id(struct scst_tgt *tgt, struct scst_session *scst_sess, uint8_t **transport_id); /* Predefs for callbacks handed to qla2xxx(target) */ -static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *pkt); -static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt); -static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha, +static void q24_atio_pkt(scsi_qla_host_t *vha, atio7_entry_t *pkt); +static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt); +static void q2t_async_event(uint16_t code, scsi_qla_host_t *vha, uint16_t *mailbox); -static void q2x_ctio_completion(scsi_qla_host_t *ha, uint32_t handle); -static int q2t_host_action(scsi_qla_host_t *ha, +static void q2x_ctio_completion(scsi_qla_host_t *vha, uint32_t handle); +static int q2t_host_action(scsi_qla_host_t *vha, qla2x_tgt_host_action_t action); -static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport); -static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport); +static void q2t_fc_port_added(scsi_qla_host_t *vha, fc_port_t *fcport); +static void q2t_fc_port_deleted(scsi_qla_host_t *vha, fc_port_t *fcport); static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun, int lun_size, int fn, void *iocb, int flags); -static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, +static void q2x_send_term_exchange(scsi_qla_host_t *vha, struct q2t_cmd *cmd, atio_entry_t *atio, int ha_locked); -static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, +static void q24_send_term_exchange(scsi_qla_host_t *vha, struct q2t_cmd *cmd, atio7_entry_t *atio, int ha_locked); -static void q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm, +static void q2t_reject_free_srr_imm(scsi_qla_host_t *vha, struct srr_imm *imm, int ha_lock); static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset); +static void q2t_schedule_sess_for_deletion(struct q2t_sess *sess); static void q2t_clear_tgt_db(struct q2t_tgt *tgt, bool immediately); static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd); static void q2t_unreg_sess(struct q2t_sess *sess); @@ -112,6 +113,7 @@ static uint16_t q2t_get_phys_transport_version(struct scst_tgt *scst_tgt); static ssize_t q2t_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); + struct kobj_attribute q2t_version_attr = __ATTR(version, S_IRUGO, q2t_version_show, NULL); @@ -143,17 +145,29 @@ static struct kobj_attribute q2t_hw_target_attr = static ssize_t q2t_node_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t q2t_node_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buffer, size_t size); static struct kobj_attribute q2t_vp_node_name_attr = __ATTR(node_name, S_IRUGO, q2t_node_name_show, NULL); -static ssize_t q2t_node_name_store(struct kobject *kobj, - struct kobj_attribute *attr, const char *buffer, size_t size); - static struct kobj_attribute q2t_hw_node_name_attr = __ATTR(node_name, S_IRUGO|S_IWUSR, q2t_node_name_show, q2t_node_name_store); +static ssize_t q2t_port_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); + +static struct kobj_attribute q2t_vp_port_name_attr = + __ATTR(port_name, S_IRUGO, q2t_port_name_show, NULL); + +static ssize_t q2t_port_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buffer, size_t size); + +static struct kobj_attribute q2t_hw_port_name_attr = + __ATTR(port_name, S_IRUGO|S_IWUSR, q2t_port_name_show, + q2t_port_name_store); + static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); @@ -165,12 +179,14 @@ static const struct attribute *q2t_hw_tgt_attrs[] = { &q2t_expl_conf_attr.attr, &q2t_abort_isp_attr.attr, &q2t_hw_node_name_attr.attr, + &q2t_hw_port_name_attr.attr, NULL, }; static const struct attribute *q2t_npiv_tgt_attrs[] = { &q2t_vp_node_name_attr.attr, &q2t_vp_parent_host_attr.attr, + &q2t_vp_port_name_attr.attr, NULL, }; @@ -178,6 +194,10 @@ static const struct attribute *q2t_npiv_tgt_attrs[] = { static int q2t_enable_tgt(struct scst_tgt *tgt, bool enable); static bool q2t_is_tgt_enabled(struct scst_tgt *tgt); + +#define ENABLE_NPIV 0 /* NPIV does not work */ + +#if ENABLE_NPIV #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED)) static ssize_t q2t_add_vtarget(const char *target_name, char *params); @@ -187,6 +207,7 @@ static ssize_t q2t_del_vtarget(const char *target_name); your kernel. Adding NPIV targets using SCST sysfs interface will be disabled. #endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED))*/ +#endif /* ENABLE_NPIV */ /* * Global Variables @@ -213,7 +234,6 @@ static struct scst_tgt_template tgt2x_template = { .rdy_to_xfer_atomic = 1, #endif .max_hw_pending_time = Q2T_MAX_HW_PENDING_TIME, - .detect = q2t_target_detect, .release = q2t_target_release, .xmit_response = q2x_xmit_response, .rdy_to_xfer = q2t_rdy_to_xfer, @@ -226,6 +246,7 @@ static struct scst_tgt_template tgt2x_template = { .on_hw_pending_cmd_timeout = q2t_on_hw_pending_cmd_timeout, .enable_target = q2t_enable_tgt, .is_target_enabled = q2t_is_tgt_enabled, +#if ENABLE_NPIV #ifndef CONFIG_SCST_PROC #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED)) @@ -236,6 +257,9 @@ static struct scst_tgt_template tgt2x_template = { .add_target_parameters = "node_name, parent_host", .tgtt_attrs = q2tt_attrs, #endif +#elif !defined(CONFIG_SCST_PROC) + .tgtt_attrs = q2tt_attrs, +#endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = Q2T_DEFAULT_LOG_FLAGS, .trace_flags = &trace_flag, @@ -257,7 +281,7 @@ static inline int scst_cmd_get_ppl_offset(struct scst_cmd *scst_cmd) } /* - * pha->hardware_lock supposed to be held on entry. + * ha->hardware_lock supposed to be held on entry. * * !! If you are calling it after finding sess in tgt->sess_list, make sure * !! that the lock is not dropped between find and this function call! @@ -268,7 +292,7 @@ static inline void q2t_sess_get(struct q2t_sess *sess) TRACE_DBG("sess %p, new sess_ref %d", sess, sess->sess_ref); } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static inline void q2t_sess_put(struct q2t_sess *sess) { TRACE_DBG("sess %p, new sess_ref %d", sess, sess->sess_ref-1); @@ -279,7 +303,7 @@ static inline void q2t_sess_put(struct q2t_sess *sess) q2t_unreg_sess(sess); } -/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ static inline struct q2t_sess *q2t_find_sess_by_loop_id(struct q2t_tgt *tgt, uint16_t loop_id) { @@ -293,7 +317,7 @@ static inline struct q2t_sess *q2t_find_sess_by_loop_id(struct q2t_tgt *tgt, return NULL; } -/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ static inline struct q2t_sess *q2t_find_sess_by_s_id_include_deleted( struct q2t_tgt *tgt, const uint8_t *s_id) { @@ -317,7 +341,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_include_deleted( return NULL; } -/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ static inline struct q2t_sess *q2t_find_sess_by_s_id(struct q2t_tgt *tgt, const uint8_t *s_id) { @@ -333,7 +357,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id(struct q2t_tgt *tgt, return NULL; } -/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt, const uint8_t *s_id) { @@ -349,7 +373,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt, return NULL; } -/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ static inline struct q2t_sess *q2t_find_sess_by_port_name(struct q2t_tgt *tgt, const uint8_t *port_name) { @@ -370,7 +394,7 @@ static inline struct q2t_sess *q2t_find_sess_by_port_name(struct q2t_tgt *tgt, return NULL; } -/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +/* ha->hardware_lock supposed to be held on entry */ static inline struct q2t_sess *q2t_find_sess_by_port_name_include_deleted( struct q2t_tgt *tgt, const uint8_t *port_name) { @@ -404,41 +428,43 @@ static inline struct q2t_sess *q2t_find_sess_by_port_name_include_deleted( return NULL; } -/* pha->hardware_lock supposed to be held on entry */ -static inline void q2t_exec_queue(scsi_qla_host_t *ha) +/* ha->hardware_lock supposed to be held on entry */ +static inline void q2t_exec_queue(scsi_qla_host_t *vha) { - qla2x00_isp_cmd(to_qla_parent(ha)); + qla2x00_start_iocbs(vha, vha->req); } -/* pha->hardware_lock supposed to be held on entry */ -static inline request_t *q2t_req_pkt(scsi_qla_host_t *ha) +/* ha->hardware_lock supposed to be held on entry */ +static inline request_t *q2t_req_pkt(scsi_qla_host_t *vha) { - return qla2x00_req_pkt(to_qla_parent(ha)); + return qla2x00_req_pkt(vha); } /* Might release hw lock, then reacquire!! */ -static inline int q2t_issue_marker(scsi_qla_host_t *ha, int ha_locked) +static inline int q2t_issue_marker(scsi_qla_host_t *vha, int vha_locked) { /* Send marker if required */ - if (unlikely(to_qla_parent(ha)->marker_needed != 0)) { - int rc = qla2x00_issue_marker(ha, ha_locked); + if (unlikely(vha->marker_needed != 0)) { + int rc = qla2x00_issue_marker(vha, vha_locked); if (rc != QLA_SUCCESS) { PRINT_ERROR("qla2x00t(%ld): issue_marker() " - "failed", ha->instance); + "failed", vha->host_no); } return rc; } return QLA_SUCCESS; } -static inline scsi_qla_host_t *q2t_find_host_by_d_id(scsi_qla_host_t *ha, +static inline scsi_qla_host_t *q2t_find_host_by_d_id(scsi_qla_host_t *vha, uint8_t *d_id) { - if ((ha->d_id.b.area != d_id[1]) || (ha->d_id.b.domain != d_id[0])) + struct qla_hw_data *ha = vha->hw; + + if ((vha->d_id.b.area != d_id[1]) || (vha->d_id.b.domain != d_id[0])) return NULL; - if (ha->d_id.b.al_pa == d_id[2]) - return ha; + if (vha->d_id.b.al_pa == d_id[2]) + return vha; if (IS_FWI2_CAPABLE(ha)) { uint8_t vp_idx; @@ -451,11 +477,13 @@ static inline scsi_qla_host_t *q2t_find_host_by_d_id(scsi_qla_host_t *ha, return NULL; } -static inline scsi_qla_host_t *q2t_find_host_by_vp_idx(scsi_qla_host_t *ha, +static inline scsi_qla_host_t *q2t_find_host_by_vp_idx(scsi_qla_host_t *vha, uint16_t vp_idx) { - if (ha->vp_idx == vp_idx) - return ha; + struct qla_hw_data *ha = vha->hw; + + if (vha->vp_idx == vp_idx) + return vha; if (IS_FWI2_CAPABLE(ha)) { sBUG_ON(ha->tgt_vp_map == NULL); @@ -466,47 +494,151 @@ static inline scsi_qla_host_t *q2t_find_host_by_vp_idx(scsi_qla_host_t *ha, return NULL; } -static void q24_atio_pkt_all_vps(scsi_qla_host_t *ha, atio7_entry_t *atio) +/* ha->hardware_lock supposed to be held on entry */ +static void q24_queue_unknown_atio(scsi_qla_host_t *vha, atio7_entry_t *atio) { + struct q2t_unknown_atio *u; + struct qla_hw_data *ha = vha->hw; + TRACE_ENTRY(); - sBUG_ON(ha == NULL); + if ((vha->tgt != NULL) && vha->tgt->tgt_stop) { + TRACE_MGMT_DBG("qla2x00t(%ld): dropping unknown ATIO_TYPE7, " + "because tgt is being stopped", vha->host_no); + goto out_term; + } + + u = kzalloc(sizeof(*u), GFP_ATOMIC); + if (u == NULL) { + TRACE(TRACE_OUT_OF_MEM, "Alloc of struct unknown_atio " + "(size %zd) failed", sizeof(*u)); + /* It should be harmless and on the next retry should work well */ + goto out_term; + } + + TRACE_MGMT_DBG("qla2x00t(%ld): Received ATIO_TYPE7 " + "with unknown d_id %x:%x:%x, queuing unknown entry %p", + vha->host_no, atio->fcp_hdr.d_id[0], atio->fcp_hdr.d_id[1], + atio->fcp_hdr.d_id[2], u); + + u->vha = vha; + memcpy(&u->atio7, atio, sizeof(u->atio7)); + + list_add_tail(&u->unknown_atio_list_entry, &ha->unknown_atio_list); + + schedule_delayed_work(&ha->unknown_atio_work, 1); + +out: + TRACE_EXIT(); + return; + +out_term: + q24_send_term_exchange(vha, NULL, atio, 1); + goto out; +} + +/* ha->hardware_lock supposed to be held on entry */ +static void q24_try_to_dequeue_unknown_atios(struct qla_hw_data *ha) +{ + struct q2t_unknown_atio *u, *t; + + TRACE_ENTRY(); + + list_for_each_entry_safe(u, t, &ha->unknown_atio_list, + unknown_atio_list_entry) { + scsi_qla_host_t *host, *vha = u->vha; + sBUG_ON(vha->hw != ha); + host = q2t_find_host_by_d_id(vha, u->atio7.fcp_hdr.d_id); + if (host != NULL) { + TRACE_MGMT_DBG("qla2x00t(%ld): Requeuing unknown " + "ATIO_TYPE7 %p", vha->host_no, u); + q24_atio_pkt(host, &u->atio7); + } else if ((vha->tgt != NULL) && vha->tgt->tgt_stop) { + TRACE_MGMT_DBG("qla2x00t(%ld): Freeing unknown " + "ATIO_TYPE7 %p, because tgt is being stopped", + vha->host_no, u); + q24_send_term_exchange(vha, NULL, &u->atio7, 1); + } else { + TRACE_DBG("u %p, vha %p, host %p, sched again..", u, + vha, host); + schedule_delayed_work(&ha->unknown_atio_work, 1); + continue; + } + + list_del(&u->unknown_atio_list_entry); + kfree(u); + } + + TRACE_EXIT(); + return; +} + +static void q2t_try_to_dequeue_unknown_atios(struct qla_hw_data *ha) +{ + unsigned long flags; + + TRACE_ENTRY(); + + TRACE_DBG("qla2x00t(%ld): Trying to requeue unknown ATIOs", + ((scsi_qla_host_t *)(pci_get_drvdata(ha->pdev)))->host_no); + + spin_lock_irqsave(&ha->hardware_lock, flags); + q24_try_to_dequeue_unknown_atios(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + TRACE_EXIT(); + return; +} + +static void __q2t_try_to_dequeue_unknown_atios(struct qla_hw_data *ha) +{ + TRACE_ENTRY(); + q2t_try_to_dequeue_unknown_atios(ha); + TRACE_EXIT(); + return; +} + +static void q24_atio_pkt_all_vps(scsi_qla_host_t *vha, atio7_entry_t *atio) +{ + struct qla_hw_data *ha = vha->hw; + + TRACE_ENTRY(); + + sBUG_ON(vha == NULL); switch (atio->entry_type) { case ATIO_TYPE7: { - scsi_qla_host_t *host = q2t_find_host_by_d_id(ha, atio->fcp_hdr.d_id); + scsi_qla_host_t *host = q2t_find_host_by_d_id(vha, atio->fcp_hdr.d_id); if (unlikely(NULL == host)) { /* * It might happen, because there is a small gap between * requesting the DPC thread to update loop and actual - * update. It is harmless and on the next retry should - * work well. + * update. */ - PRINT_WARNING("qla2x00t(%ld): Received ATIO_TYPE7 " - "with unknown d_id %x:%x:%x", ha->instance, - atio->fcp_hdr.d_id[0], atio->fcp_hdr.d_id[1], - atio->fcp_hdr.d_id[2]); + q24_queue_unknown_atio(vha, atio); break; } + if (unlikely(!list_empty(&ha->unknown_atio_list))) + q24_try_to_dequeue_unknown_atios(ha); q24_atio_pkt(host, atio); break; } case IMMED_NOTIFY_TYPE: { - scsi_qla_host_t *host = ha; + scsi_qla_host_t *host = vha; if (IS_FWI2_CAPABLE(ha)) { notify24xx_entry_t *entry = (notify24xx_entry_t *)atio; if ((entry->vp_index != 0xFF) && (entry->nport_handle != 0xFFFF)) { - host = q2t_find_host_by_vp_idx(ha, + host = q2t_find_host_by_vp_idx(vha, entry->vp_index); if (unlikely(!host)) { PRINT_ERROR("qla2x00t(%ld): Received " "ATIO (IMMED_NOTIFY_TYPE) " "with unknown vp_index %d", - ha->instance, entry->vp_index); + vha->host_no, entry->vp_index); break; } } @@ -517,7 +649,7 @@ static void q24_atio_pkt_all_vps(scsi_qla_host_t *ha, atio7_entry_t *atio) default: PRINT_ERROR("qla2x00t(%ld): Received unknown ATIO atio " - "type %x", ha->instance, atio->entry_type); + "type %x", vha->host_no, atio->entry_type); break; } @@ -525,22 +657,24 @@ static void q24_atio_pkt_all_vps(scsi_qla_host_t *ha, atio7_entry_t *atio) return; } -static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) +static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt) { + struct qla_hw_data *ha = vha->hw; + TRACE_ENTRY(); - sBUG_ON(ha == NULL); + sBUG_ON(vha == NULL); switch (pkt->entry_type) { case CTIO_TYPE7: { ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt; - scsi_qla_host_t *host = q2t_find_host_by_vp_idx(ha, + scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha, entry->vp_index); if (unlikely(!host)) { PRINT_ERROR("qla2x00t(%ld): Response pkt (CTIO_TYPE7) " "received, with unknown vp_index %d", - ha->instance, entry->vp_index); + vha->host_no, entry->vp_index); break; } q2t_response_pkt(host, pkt); @@ -549,15 +683,15 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) case IMMED_NOTIFY_TYPE: { - scsi_qla_host_t *host = ha; + scsi_qla_host_t *host = vha; if (IS_FWI2_CAPABLE(ha)) { notify24xx_entry_t *entry = (notify24xx_entry_t *)pkt; - host = q2t_find_host_by_vp_idx(ha, entry->vp_index); + host = q2t_find_host_by_vp_idx(vha, entry->vp_index); if (unlikely(!host)) { PRINT_ERROR("qla2x00t(%ld): Response pkt " "(IMMED_NOTIFY_TYPE) received, " "with unknown vp_index %d", - ha->instance, entry->vp_index); + vha->host_no, entry->vp_index); break; } } @@ -567,17 +701,17 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) case NOTIFY_ACK_TYPE: { - scsi_qla_host_t *host = ha; + scsi_qla_host_t *host = vha; if (IS_FWI2_CAPABLE(ha)) { nack24xx_entry_t *entry = (nack24xx_entry_t *)pkt; if (0xFF != entry->vp_index) { - host = q2t_find_host_by_vp_idx(ha, + host = q2t_find_host_by_vp_idx(vha, entry->vp_index); if (unlikely(!host)) { PRINT_ERROR("qla2x00t(%ld): Response " "pkt (NOTIFY_ACK_TYPE) " "received, with unknown " - "vp_index %d", ha->instance, + "vp_index %d", vha->host_no, entry->vp_index); break; } @@ -590,12 +724,12 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) case ABTS_RECV_24XX: { abts24_recv_entry_t *entry = (abts24_recv_entry_t *)pkt; - scsi_qla_host_t *host = q2t_find_host_by_vp_idx(ha, + scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha, entry->vp_index); if (unlikely(!host)) { PRINT_ERROR("qla2x00t(%ld): Response pkt " "(ABTS_RECV_24XX) received, with unknown " - "vp_index %d", ha->instance, entry->vp_index); + "vp_index %d", vha->host_no, entry->vp_index); break; } q2t_response_pkt(host, pkt); @@ -605,12 +739,12 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) case ABTS_RESP_24XX: { abts24_resp_entry_t *entry = (abts24_resp_entry_t *)pkt; - scsi_qla_host_t *host = q2t_find_host_by_vp_idx(ha, + scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha, entry->vp_index); if (unlikely(!host)) { PRINT_ERROR("qla2x00t(%ld): Response pkt " "(ABTS_RECV_24XX) received, with unknown " - "vp_index %d", ha->instance, entry->vp_index); + "vp_index %d", vha->host_no, entry->vp_index); break; } q2t_response_pkt(host, pkt); @@ -618,7 +752,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) } default: - q2t_response_pkt(ha, pkt); + q2t_response_pkt(vha, pkt); break; } @@ -629,7 +763,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) * Registers with initiator driver (but target mode isn't enabled till * it's turned on via sysfs) */ -static int q2t_target_detect(struct scst_tgt_template *tgtt) +static int q2t_target_driver_reg(struct scst_tgt_template *tgtt) { int res, rc; struct qla_tgt_data t = { @@ -638,6 +772,7 @@ static int q2t_target_detect(struct scst_tgt_template *tgtt) .tgt_response_pkt = q2t_response_pkt_all_vps, .tgt2x_ctio_completion = q2x_ctio_completion, .tgt_async_event = q2t_async_event, + .tgt_try_to_dequeue_unknown_atios = __q2t_try_to_dequeue_unknown_atios, .tgt_host_action = q2t_host_action, .tgt_fc_port_added = q2t_fc_port_added, .tgt_fc_port_deleted = q2t_fc_port_deleted, @@ -659,8 +794,6 @@ static int q2t_target_detect(struct scst_tgt_template *tgtt) goto out; } - qla2xxx_add_targets(); - res = 0; PRINT_INFO("qla2x00t: %s", "Target mode driver for QLogic 2x00 controller " @@ -675,7 +808,8 @@ static void q2t_free_session_done(struct scst_session *scst_sess) { struct q2t_sess *sess; struct q2t_tgt *tgt; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; unsigned long flags; TRACE_ENTRY(); @@ -695,25 +829,25 @@ static void q2t_free_session_done(struct scst_session *scst_sess) TRACE_DBG("empty(sess_list) %d sess_count %d", list_empty(&tgt->sess_list), tgt->sess_count); - ha = tgt->ha; - pha = to_qla_parent(ha); + vha = tgt->vha; + ha = vha->hw; /* * We need to protect against race, when tgt is freed before or * inside wake_up() */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); tgt->sess_count--; if (tgt->sess_count == 0) wake_up_all(&tgt->waitQ); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); out: TRACE_EXIT(); return; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static void q2t_unreg_sess(struct q2t_sess *sess) { TRACE_ENTRY(); @@ -726,7 +860,7 @@ static void q2t_unreg_sess(struct q2t_sess *sess) sBUG_ON(list_entry_in_list(&sess->sess_list_entry)); PRINT_INFO("qla2x00t(%ld): %ssession for loop_id %d deleted", - sess->tgt->ha->instance, sess->local ? "local " : "", + sess->tgt->vha->host_no, sess->local ? "local " : "", sess->loop_id); scst_unregister_session(sess->scst_sess, 0, q2t_free_session_done); @@ -735,29 +869,40 @@ static void q2t_unreg_sess(struct q2t_sess *sess) return; } -/* pha->hardware_lock supposed to be held on entry */ -static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd) +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_reset(scsi_qla_host_t *vha, void *iocb, int mcmd) { struct q2t_sess *sess; int loop_id; uint16_t lun = 0; int res = 0; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); if (IS_FWI2_CAPABLE(ha)) { notify24xx_entry_t *n = (notify24xx_entry_t *)iocb; - loop_id = le16_to_cpu(n->nport_handle); + if ((le16_to_cpu(n->status) == IMM_NTFY_ELS) && + ((n->status_subcode == ELS_TPRLO) || + (n->status_subcode == ELS_LOGO))) { + if ((n->status_subcode == ELS_TPRLO) && + (n->flags & __constant_cpu_to_le16(BIT_1))) { + /* Global flag set */ + loop_id = 0xFFFF; + } else + loop_id = le16_to_cpu(n->els_nport_handle); + } else + loop_id = le16_to_cpu(n->nport_handle); } else loop_id = GET_TARGET_ID(ha, (notify_entry_t *)iocb); if (loop_id == 0xFFFF) { /* Global event */ - atomic_inc(&ha->tgt->tgt_global_resets_count); - q2t_clear_tgt_db(ha->tgt, false); - if (!list_empty(&ha->tgt->sess_list)) { - sess = list_first_entry(&ha->tgt->sess_list, - typeof(*sess), sess_list_entry); + atomic_inc(&vha->tgt->tgt_global_resets_count); + q2t_clear_tgt_db(vha->tgt, false); + if (!list_empty(&vha->tgt->sess_list)) { + sess = list_entry(vha->tgt->sess_list.next, + typeof(*sess), sess_list_entry); switch (mcmd) { case Q2T_NEXUS_LOSS_SESS: mcmd = Q2T_NEXUS_LOSS; @@ -773,7 +918,7 @@ static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd) default: PRINT_ERROR("qla2x00t(%ld): Not allowed " - "command %x in %s", ha->instance, + "command %x in %s", vha->host_no, mcmd, __func__); sess = NULL; break; @@ -781,17 +926,19 @@ static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd) } else sess = NULL; } else - sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); + sess = q2t_find_sess_by_loop_id(vha->tgt, loop_id); if (sess == NULL) { + if (loop_id != 0xFFFF) + TRACE_MGMT_DBG("sess for loop_id 0x%x not found", loop_id); res = -ESRCH; - ha->tgt->tm_to_unknown = 1; - goto out; + vha->tgt->tm_to_unknown = 1; + goto del; } TRACE_MGMT_DBG("scsi(%ld): resetting (session %p from port " "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, " - "mcmd %x, loop_id %d)", ha->host_no, sess, + "mcmd %x, loop_id %d)", vha->host_no, sess, sess->port_name[0], sess->port_name[1], sess->port_name[2], sess->port_name[3], sess->port_name[4], sess->port_name[5], @@ -801,12 +948,24 @@ static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd) res = q2t_issue_task_mgmt(sess, (uint8_t *)&lun, sizeof(lun), mcmd, iocb, Q24_MGMT_SEND_NACK); -out: +del: + switch (mcmd) { + case Q2T_NEXUS_LOSS_SESS: + if (sess != NULL) + q2t_schedule_sess_for_deletion(sess); + break; + case Q2T_NEXUS_LOSS: + if (loop_id != 0xFFFF) + q2t_clear_tgt_db(vha->tgt, false); + default: + break; + } + TRACE_EXIT_RES(res); return res; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static void q2t_sess_del(struct q2t_sess *sess) { TRACE_ENTRY(); @@ -825,7 +984,7 @@ static void q2t_sess_del(struct q2t_sess *sess) static void q2t_schedule_sess_for_deletion(struct q2t_sess *sess) { struct q2t_tgt *tgt = sess->tgt; - uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5; + uint32_t dev_loss_tmo = tgt->vha->hw->port_down_retry_count + 5; bool schedule; TRACE_ENTRY(); @@ -853,8 +1012,8 @@ static void q2t_schedule_sess_for_deletion(struct q2t_sess *sess) sess->expires = jiffies + dev_loss_tmo * HZ; PRINT_INFO("qla2x00t(%ld): session for port %02x:%02x:%02x:" - "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for " - "deletion in %d secs", tgt->ha->instance, + "%02x:%02x:%02x:%02x:%02x (loop_id %d) scheduled for " + "deletion in %d secs", tgt->vha->host_no, sess->port_name[0], sess->port_name[1], sess->port_name[2], sess->port_name[3], sess->port_name[4], sess->port_name[5], @@ -863,7 +1022,7 @@ static void q2t_schedule_sess_for_deletion(struct q2t_sess *sess) if (schedule) schedule_delayed_work(&tgt->sess_del_work, - sess->expires - jiffies); + max_t(long, sess->expires - jiffies, 0)); out: TRACE_EXIT(); @@ -873,28 +1032,29 @@ out: static int q2t_close_session(struct scst_session *scst_sess) { struct q2t_sess *sess = scst_sess_get_tgt_priv(scst_sess); - scsi_qla_host_t *ha = sess->tgt->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct q2t_tgt *tgt = sess->tgt; + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; unsigned long flags; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); if (list_entry_in_list(&sess->sess_list_entry)) { TRACE_MGMT_DBG("Force closing sess %p", sess); q2t_sess_del(sess); } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return 0; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static void q2t_clear_tgt_db(struct q2t_tgt *tgt, bool immediately) { struct q2t_sess *sess, *sess_tmp; TRACE_ENTRY(); - TRACE(TRACE_MGMT, "qla2x00t: Clearing targets DB for target %p", tgt); + TRACE_MGMT_DBG("qla2x00t: Clearing targets DB for target %p", tgt); list_for_each_entry_safe(sess, sess_tmp, &tgt->sess_list, sess_list_entry) { @@ -929,30 +1089,30 @@ static void q2t_alloc_session_done(struct scst_session *scst_sess, { struct q2t_sess *sess = (struct q2t_sess *)data; struct q2t_tgt *tgt = sess->tgt; - scsi_qla_host_t *ha = tgt->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; unsigned long flags; TRACE_ENTRY(); - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); if (result != 0) { PRINT_INFO("qla2x00t(%ld): Session initialization failed", - ha->instance); + vha->host_no); if (list_entry_in_list(&sess->sess_list_entry)) q2t_sess_del(sess); } q2t_sess_put(sess); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); TRACE_EXIT(); return; } -static int q24_get_loop_id(scsi_qla_host_t *ha, const uint8_t *s_id, +static int q24_get_loop_id(scsi_qla_host_t *vha, const uint8_t *s_id, uint16_t *loop_id) { dma_addr_t gid_list_dma; @@ -960,21 +1120,22 @@ static int q24_get_loop_id(scsi_qla_host_t *ha, const uint8_t *s_id, char *id_iter; int res, rc, i, retries = 0; uint16_t entries; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, + gid_list = dma_alloc_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), &gid_list_dma, GFP_KERNEL); if (gid_list == NULL) { - PRINT_ERROR("qla2x00t(%ld): DMA Alloc failed of %zd", - ha->instance, GID_LIST_SIZE); + PRINT_ERROR("qla2x00t(%ld): DMA Alloc failed of %d", + vha->host_no, qla2x00_gid_list_size(ha)); res = -ENOMEM; goto out; } /* Get list of logged in devices */ retry: - rc = qla2x00_get_id_list(ha, gid_list, gid_list_dma, &entries); + rc = qla2x00_get_id_list(vha, gid_list, gid_list_dma, &entries); if (rc != QLA_SUCCESS) { if (rc == QLA_FW_NOT_READY) { retries++; @@ -984,7 +1145,7 @@ retry: } } TRACE_MGMT_DBG("qla2x00t(%ld): get_id_list() failed: %x", - ha->instance, rc); + vha->host_no, rc); res = -rc; goto out_free_id_list; } @@ -1004,15 +1165,16 @@ retry: } out_free_id_list: - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, gid_list, gid_list_dma); + dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), gid_list, gid_list_dma); out: TRACE_EXIT_RES(res); return res; } -static bool q2t_check_fcport_exist(scsi_qla_host_t *ha, struct q2t_sess *sess) +static bool q2t_check_fcport_exist(scsi_qla_host_t *vha, struct q2t_sess *sess) { + struct qla_hw_data *ha = vha->hw; bool res, found = false; int rc, i; uint16_t loop_id = 0xFFFF; /* to eliminate compiler's warning */ @@ -1025,9 +1187,9 @@ static bool q2t_check_fcport_exist(scsi_qla_host_t *ha, struct q2t_sess *sess) TRACE_ENTRY(); retry: - global_resets = atomic_read(&ha->tgt->tgt_global_resets_count); + global_resets = atomic_read(&vha->tgt->tgt_global_resets_count); - rc = qla2x00_get_node_name_list(ha, &pmap, &pmap_len); + rc = qla2x00_get_node_name_list(vha, true, &pmap, &pmap_len); if (rc != QLA_SUCCESS) { res = false; goto out; @@ -1039,14 +1201,14 @@ retry: entries = pmap_len/sizeof(*pmap24); for (i = 0; i < entries; ++i) { - if ((sess->port_name[0] == pmap24[i].port_name[0]) && - (sess->port_name[1] == pmap24[i].port_name[1]) && - (sess->port_name[2] == pmap24[i].port_name[2]) && - (sess->port_name[3] == pmap24[i].port_name[3]) && - (sess->port_name[4] == pmap24[i].port_name[4]) && - (sess->port_name[5] == pmap24[i].port_name[5]) && - (sess->port_name[6] == pmap24[i].port_name[6]) && - (sess->port_name[7] == pmap24[i].port_name[7])) { + if ((sess->port_name[0] == pmap24[i].port_name[7]) && + (sess->port_name[1] == pmap24[i].port_name[6]) && + (sess->port_name[2] == pmap24[i].port_name[5]) && + (sess->port_name[3] == pmap24[i].port_name[4]) && + (sess->port_name[4] == pmap24[i].port_name[3]) && + (sess->port_name[5] == pmap24[i].port_name[2]) && + (sess->port_name[6] == pmap24[i].port_name[1]) && + (sess->port_name[7] == pmap24[i].port_name[0])) { loop_id = le16_to_cpu(pmap24[i].loop_id); found = true; break; @@ -1058,14 +1220,14 @@ retry: entries = pmap_len/sizeof(*pmap2x); for (i = 0; i < entries; ++i) { - if ((sess->port_name[0] == pmap2x[i].port_name[0]) && - (sess->port_name[1] == pmap2x[i].port_name[1]) && - (sess->port_name[2] == pmap2x[i].port_name[2]) && - (sess->port_name[3] == pmap2x[i].port_name[3]) && - (sess->port_name[4] == pmap2x[i].port_name[4]) && - (sess->port_name[5] == pmap2x[i].port_name[5]) && - (sess->port_name[6] == pmap2x[i].port_name[6]) && - (sess->port_name[7] == pmap2x[i].port_name[7])) { + if ((sess->port_name[0] == pmap2x[i].port_name[7]) && + (sess->port_name[1] == pmap2x[i].port_name[6]) && + (sess->port_name[2] == pmap2x[i].port_name[5]) && + (sess->port_name[3] == pmap2x[i].port_name[4]) && + (sess->port_name[4] == pmap2x[i].port_name[3]) && + (sess->port_name[5] == pmap2x[i].port_name[2]) && + (sess->port_name[6] == pmap2x[i].port_name[1]) && + (sess->port_name[7] == pmap2x[i].port_name[0])) { loop_id = le16_to_cpu(pmap2x[i].loop_id); found = true; break; @@ -1080,32 +1242,43 @@ retry: goto out; } - TRACE_MGMT_DBG("loop_id %d", loop_id); + TRACE_DBG("loop_id 0x%x", loop_id); fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); if (fcport == NULL) { PRINT_ERROR("qla2x00t(%ld): Allocation of tmp FC port failed", - ha->instance); + vha->host_no); res = false; goto out; } - fcport->loop_id = loop_id; + /* + * Mask that bit off, because the driver/fw does not use + * it for internal loop_id lookups. + * + * From 3.47 in the Qlogic 2500 fw spec: + * + * Internet protocol (IP) sets bit 15 of the N_Port handle to + * indicate that the device completed port login, but not + * process login. The bit setting allows the host to see the + * device as an IP candidate. + */ + fcport->loop_id = loop_id & 0x7FFF; - rc = qla2x00_get_port_database(ha, fcport, 0); + rc = qla2x00_get_port_database(vha, fcport, 0); if (rc != QLA_SUCCESS) { PRINT_ERROR("qla2x00t(%ld): Failed to retrieve fcport " "information -- get_port_database() returned %x " - "(loop_id=0x%04x)", ha->instance, rc, loop_id); + "(loop_id=0x%04x)", vha->host_no, rc, loop_id); res = false; goto out_free_fcport; } - if (global_resets != atomic_read(&ha->tgt->tgt_global_resets_count)) { + if (global_resets != atomic_read(&vha->tgt->tgt_global_resets_count)) { TRACE_MGMT_DBG("qla2x00t(%ld): global reset during session " "discovery (counter was %d, new %d), retrying", - ha->instance, global_resets, - atomic_read(&ha->tgt->tgt_global_resets_count)); + vha->host_no, global_resets, + atomic_read(&vha->tgt->tgt_global_resets_count)); goto retry; } @@ -1129,7 +1302,7 @@ out: return res; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static void q2t_undelete_sess(struct q2t_sess *sess) { TRACE_ENTRY(); @@ -1149,14 +1322,14 @@ static void q2t_del_sess_work_fn(struct work_struct *work) { struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, sess_del_work.work); - scsi_qla_host_t *ha = tgt->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; struct q2t_sess *sess; unsigned long flags; TRACE_ENTRY(); - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); while (!list_empty(&tgt->del_sess_list)) { sess = list_first_entry(&tgt->del_sess_list, typeof(*sess), sess_list_entry); @@ -1171,9 +1344,9 @@ static void q2t_del_sess_work_fn(struct work_struct *work) */ q2t_sess_get(sess); - spin_unlock_irqrestore(&pha->hardware_lock, flags); - cancel = q2t_check_fcport_exist(ha, sess); - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + cancel = q2t_check_fcport_exist(vha, sess); + spin_lock_irqsave(&ha->hardware_lock, flags); if (!sess->deleted || !list_entry_in_list(&sess->sess_list_entry)) { @@ -1187,11 +1360,11 @@ static void q2t_del_sess_work_fn(struct work_struct *work) if (cancel) { q2t_undelete_sess(sess); - PRINT_INFO("qla2x00t(%ld): cancel deletion of " + TRACE(TRACE_MINOR, "qla2x00t(%ld): cancel deletion of " "session for port %02x:%02x:%02x:" - "%02x:%02x:%02x:%02x:%02x (loop ID %d), " + "%02x:%02x:%02x:%02x:%02x (loop_id %d), " "because it isn't deleted by firmware", - ha->instance, + vha->host_no, sess->port_name[0], sess->port_name[1], sess->port_name[2], sess->port_name[3], sess->port_name[4], sess->port_name[5], @@ -1205,11 +1378,11 @@ put_continue: q2t_sess_put(sess); } else { schedule_delayed_work(&tgt->sess_del_work, - sess->expires - jiffies); + max_t(long, sess->expires - jiffies, 0)); break; } } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); TRACE_EXIT(); return; @@ -1221,18 +1394,18 @@ put_continue: * Adds an extra ref to allow to drop hw lock after adding sess to the list. * Caller must put it. */ -static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, +static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *vha, fc_port_t *fcport, bool local) { char *wwn_str; - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; struct q2t_sess *sess; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); /* Check to avoid double sessions */ - spin_lock_irq(&pha->hardware_lock); + spin_lock_irq(&ha->hardware_lock); sess = q2t_find_sess_by_port_name_include_deleted(tgt, fcport->port_name); if (sess != NULL) { TRACE_MGMT_DBG("Double sess %p found (s_id %x:%x:%x, " @@ -1252,10 +1425,10 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, sess->conf_compl_supported = fcport->conf_compl_supported; if (sess->local && !local) sess->local = 0; - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); goto out; } - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); /* We are under tgt_mutex, so a new sess can't be added behind us */ @@ -1263,7 +1436,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, if (sess == NULL) { PRINT_ERROR("qla2x00t(%ld): session allocation failed, " "all commands from port %02x:%02x:%02x:%02x:" - "%02x:%02x:%02x:%02x will be refused", ha->instance, + "%02x:%02x:%02x:%02x will be refused", vha->host_no, fcport->port_name[0], fcport->port_name[1], fcport->port_name[2], fcport->port_name[3], fcport->port_name[4], fcport->port_name[5], @@ -1273,7 +1446,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, /* +1 for q2t_alloc_session_done(), +1 extra ref, see above */ sess->sess_ref = 3; - sess->tgt = ha->tgt; + sess->tgt = vha->tgt; sess->s_id = fcport->d_id; sess->loop_id = fcport->loop_id; sess->conf_compl_supported = fcport->conf_compl_supported; @@ -1290,7 +1463,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, if (wwn_str == NULL) { PRINT_ERROR("qla2x00t(%ld): Allocation of wwn_str failed. " "All commands from port %02x:%02x:%02x:%02x:%02x:%02x:" - "%02x:%02x will be refused", ha->instance, + "%02x:%02x will be refused", vha->host_no, fcport->port_name[0], fcport->port_name[1], fcport->port_name[2], fcport->port_name[3], fcport->port_name[4], fcport->port_name[5], @@ -1299,7 +1472,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, } /* Lock here to eliminate creating sessions for being stopped target */ - spin_lock_irq(&pha->hardware_lock); + spin_lock_irq(&ha->hardware_lock); if (tgt->tgt_stop) goto out_free_sess_wwn; @@ -1308,9 +1481,9 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, sess, sess, q2t_alloc_session_done); if (sess->scst_sess == NULL) { PRINT_ERROR("qla2x00t(%ld): scst_register_session() " - "failed for host %ld (wwn %s, loop_id %d), all " - "commands from it will be refused", ha->instance, - ha->host_no, wwn_str, fcport->loop_id); + "failed for (wwn %s, loop_id %d), all " + "commands from it will be refused", vha->host_no, + wwn_str, fcport->loop_id); goto out_free_sess_wwn; } @@ -1318,11 +1491,11 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, list_add_tail(&sess->sess_list_entry, &tgt->sess_list); tgt->sess_count++; - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); PRINT_INFO("qla2x00t(%ld): %ssession for wwn %s (loop_id %d, " "s_id %x:%x:%x, confirmed completion %ssupported) added", - ha->instance, local ? "local " : "", wwn_str, fcport->loop_id, + vha->host_no, local ? "local " : "", wwn_str, fcport->loop_id, sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa, sess->conf_compl_supported ? "" : "not "); @@ -1333,7 +1506,7 @@ out: return sess; out_free_sess_wwn: - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); kfree(wwn_str); /* go through */ @@ -1344,17 +1517,17 @@ out_free_sess: goto out; } -static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) +static void q2t_fc_port_added(scsi_qla_host_t *vha, fc_port_t *fcport) { struct q2t_tgt *tgt; struct q2t_sess *sess; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - mutex_lock(&ha->tgt_mutex); + mutex_lock(&vha->tgt_mutex); - tgt = ha->tgt; + tgt = vha->tgt; if ((tgt == NULL) || (fcport->port_type != FCT_INITIATOR)) goto out_unlock; @@ -1362,13 +1535,13 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) if (tgt->tgt_stop) goto out_unlock; - spin_lock_irq(&pha->hardware_lock); + spin_lock_irq(&ha->hardware_lock); sess = q2t_find_sess_by_port_name_include_deleted(tgt, fcport->port_name); if (sess == NULL) { - spin_unlock_irq(&pha->hardware_lock); - sess = q2t_create_sess(ha, fcport, false); - spin_lock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); + sess = q2t_create_sess(vha, fcport, false); + spin_lock_irq(&ha->hardware_lock); if (sess != NULL) q2t_sess_put(sess); /* put the extra creation ref */ } else { @@ -1376,8 +1549,8 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) q2t_undelete_sess(sess); PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:" - "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) " - "reappeared", sess->tgt->ha->instance, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop_id %d) " + "reappeared", sess->tgt->vha->host_no, sess->local ? "local " : "", sess->port_name[0], sess->port_name[1], sess->port_name[2], sess->port_name[3], sess->port_name[4], @@ -1394,7 +1567,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) if (sess && sess->local) { TRACE(TRACE_MGMT, "qla2x00t(%ld): local session for " "port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x " - "(loop ID %d) became global", ha->instance, + "(loop_id %d) became global", vha->host_no, fcport->port_name[0], fcport->port_name[1], fcport->port_name[2], fcport->port_name[3], fcport->port_name[4], fcport->port_name[5], @@ -1403,26 +1576,26 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) sess->local = 0; } - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); out_unlock: - mutex_unlock(&ha->tgt_mutex); + mutex_unlock(&vha->tgt_mutex); TRACE_EXIT(); return; } -static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) +static void q2t_fc_port_deleted(scsi_qla_host_t *vha, fc_port_t *fcport) { struct q2t_tgt *tgt; struct q2t_sess *sess; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - mutex_lock(&ha->tgt_mutex); + mutex_lock(&vha->tgt_mutex); - tgt = ha->tgt; + tgt = vha->tgt; if ((tgt == NULL) || (fcport->port_type != FCT_INITIATOR)) goto out_unlock; @@ -1430,7 +1603,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) if (tgt->tgt_stop) goto out_unlock; - spin_lock_irq(&pha->hardware_lock); + spin_lock_irq(&ha->hardware_lock); sess = q2t_find_sess_by_port_name(tgt, fcport->port_name); if (sess == NULL) @@ -1442,10 +1615,10 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) q2t_schedule_sess_for_deletion(sess); out_unlock_ha: - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); out_unlock: - mutex_unlock(&ha->tgt_mutex); + mutex_unlock(&vha->tgt_mutex); TRACE_EXIT(); return; @@ -1455,17 +1628,17 @@ static inline int test_tgt_sess_count(struct q2t_tgt *tgt) { unsigned long flags; int res; - scsi_qla_host_t *pha = to_qla_parent(tgt->ha); + struct qla_hw_data *ha = tgt->vha->hw; /* * We need to protect against race, when tgt is freed before or * inside wake_up() */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); TRACE_DBG("tgt %p, empty(sess_list)=%d sess_count=%d", tgt, list_empty(&tgt->sess_list), tgt->sess_count); res = (tgt->sess_count == 0); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return res; } @@ -1474,27 +1647,34 @@ static inline int test_tgt_sess_count(struct q2t_tgt *tgt) static void q2t_target_stop(struct scst_tgt *scst_tgt) { struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); - scsi_qla_host_t *ha = tgt->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - TRACE_DBG("Stopping target for host %ld(%p)", ha->host_no, ha); + TRACE_DBG("Stopping target for host %ld(%p)", vha->host_no, ha); /* * Mutex needed to sync with q2t_fc_port_[added,deleted]. * Lock is needed, because we still can get an incoming packet. */ - mutex_lock(&ha->tgt_mutex); - spin_lock_irq(&pha->hardware_lock); + mutex_lock(&vha->tgt_mutex); + spin_lock_irq(&ha->hardware_lock); tgt->tgt_stop = 1; q2t_clear_tgt_db(tgt, true); - spin_unlock_irq(&pha->hardware_lock); - mutex_unlock(&ha->tgt_mutex); + spin_unlock_irq(&ha->hardware_lock); + mutex_unlock(&vha->tgt_mutex); + cancel_delayed_work_sync(&ha->unknown_atio_work); cancel_delayed_work_sync(&tgt->sess_del_work); + /* + * Just in case, if unknown_atio_work was canceled before clearing + * the unknown ATIOs + */ + q2t_try_to_dequeue_unknown_atios(ha); + TRACE_MGMT_DBG("Waiting for sess works (tgt %p)", tgt); spin_lock_irq(&tgt->sess_work_lock); while (!list_empty(&tgt->sess_works_list)) { @@ -1510,9 +1690,8 @@ static void q2t_target_stop(struct scst_tgt *scst_tgt) wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); - /* Big hammer */ - if (!pha->host_shutting_down && qla_tgt_mode_enabled(ha)) - qla2x00_disable_tgt_mode(ha); + if (!vha->hw->host_shutting_down && qla_tgt_mode_enabled(vha)) + qla2x00_disable_tgt_mode(vha); /* Wait for sessions to clear out (just in case) */ wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); @@ -1520,16 +1699,16 @@ static void q2t_target_stop(struct scst_tgt *scst_tgt) TRACE_MGMT_DBG("Waiting for %d IRQ commands to complete (tgt %p)", tgt->irq_cmd_count, tgt); - mutex_lock(&ha->tgt_mutex); - spin_lock_irq(&pha->hardware_lock); + mutex_lock(&vha->tgt_mutex); + spin_lock_irq(&ha->hardware_lock); while (tgt->irq_cmd_count != 0) { - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); udelay(2); - spin_lock_irq(&pha->hardware_lock); + spin_lock_irq(&ha->hardware_lock); } - ha->tgt = NULL; - spin_unlock_irq(&pha->hardware_lock); - mutex_unlock(&ha->tgt_mutex); + vha->tgt = NULL; + spin_unlock_irq(&ha->hardware_lock); + mutex_unlock(&vha->tgt_mutex); TRACE_MGMT_DBG("Stop of tgt %p finished", tgt); @@ -1541,7 +1720,7 @@ static void q2t_target_stop(struct scst_tgt *scst_tgt) static int q2t_target_release(struct scst_tgt *scst_tgt) { struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); - scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = tgt->vha; TRACE_ENTRY(); @@ -1549,7 +1728,7 @@ static int q2t_target_release(struct scst_tgt *scst_tgt) cancel_work_sync(&tgt->rscn_reg_work); - ha->q2t_tgt = NULL; + vha->q2t_tgt = NULL; scst_tgt_set_tgt_priv(scst_tgt, NULL); TRACE_MGMT_DBG("Release of tgt %p finished", tgt); @@ -1560,7 +1739,7 @@ static int q2t_target_release(struct scst_tgt *scst_tgt) return 0; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static int q2t_sched_sess_work(struct q2t_tgt *tgt, int type, const void *param, unsigned int param_size) { @@ -1573,7 +1752,7 @@ static int q2t_sched_sess_work(struct q2t_tgt *tgt, int type, prm = kzalloc(sizeof(*prm), GFP_ATOMIC); if (prm == NULL) { PRINT_ERROR("qla2x00t(%ld): Unable to create session " - "work, command will be refused", tgt->ha->instance); + "work, command will be refused", tgt->vha->host_no); res = -ENOMEM; goto out; } @@ -1604,28 +1783,28 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q2x_modify_command_count(scsi_qla_host_t *ha, int cmd_count, +static void q2x_modify_command_count(scsi_qla_host_t *vha, int cmd_count, int imm_count) { modify_lun_entry_t *pkt; TRACE_ENTRY(); - TRACE_DBG("Sending MODIFY_LUN (ha=%p, cmd=%d, imm=%d)", - ha, cmd_count, imm_count); + TRACE_DBG("Sending MODIFY_LUN (vha=%p, cmd=%d, imm=%d)", + vha, cmd_count, imm_count); /* Sending marker isn't necessary, since we called from ISR */ - pkt = (modify_lun_entry_t *)q2t_req_pkt(ha); + pkt = (modify_lun_entry_t *)q2t_req_pkt(vha); if (pkt == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } - ha->tgt->modify_lun_expected++; + vha->tgt->modify_lun_expected++; pkt->entry_type = MODIFY_LUN_TYPE; pkt->entry_count = 1; @@ -1649,7 +1828,7 @@ static void q2x_modify_command_count(scsi_qla_host_t *ha, int cmd_count, TRACE_BUFFER("MODIFY LUN packet data", pkt, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out: TRACE_EXIT(); @@ -1657,31 +1836,32 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q2x_send_notify_ack(scsi_qla_host_t *ha, notify_entry_t *iocb, +static void q2x_send_notify_ack(scsi_qla_host_t *vha, notify_entry_t *iocb, uint32_t add_flags, uint16_t resp_code, int resp_code_valid, uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan) { nack_entry_t *ntfy; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - TRACE_DBG("Sending NOTIFY_ACK (ha=%p)", ha); + TRACE_DBG("Sending NOTIFY_ACK (vha=%p)", vha); /* Send marker if required */ - if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + if (q2t_issue_marker(vha, 1) != QLA_SUCCESS) goto out; - ntfy = (nack_entry_t *)q2t_req_pkt(ha); + ntfy = (nack_entry_t *)q2t_req_pkt(vha); if (ntfy == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } - if (ha->tgt != NULL) - ha->tgt->notify_ack_expected++; + if (vha->tgt != NULL) + vha->tgt->notify_ack_expected++; ntfy->entry_type = NOTIFY_ACK_TYPE; ntfy->entry_count = 1; @@ -1690,7 +1870,7 @@ static void q2x_send_notify_ack(scsi_qla_host_t *ha, notify_entry_t *iocb, ntfy->task_flags = iocb->task_flags; ntfy->seq_id = iocb->seq_id; /* Do not increment here, the chip isn't decrementing */ - /* ntfy->flags = cpu_to_le16(NOTIFY_ACK_RES_COUNT); */ + /* ntfy->flags = __constant_cpu_to_le16(NOTIFY_ACK_RES_COUNT); */ ntfy->flags |= cpu_to_le16(add_flags); ntfy->srr_rx_id = iocb->srr_rx_id; ntfy->srr_rel_offs = iocb->srr_rel_offs; @@ -1702,16 +1882,16 @@ static void q2x_send_notify_ack(scsi_qla_host_t *ha, notify_entry_t *iocb, if (resp_code_valid) { ntfy->resp_code = cpu_to_le16(resp_code); - ntfy->flags |= cpu_to_le16(NOTIFY_ACK_TM_RESP_CODE_VALID); + ntfy->flags |= __constant_cpu_to_le16(NOTIFY_ACK_TM_RESP_CODE_VALID); } TRACE(TRACE_SCSI, "qla2x00t(%ld): Sending Notify Ack Seq %#x -> I %#x " - "St %#x RC %#x", ha->instance, + "St %#x RC %#x", vha->host_no, le16_to_cpu(iocb->seq_id), GET_TARGET_ID(ha, iocb), le16_to_cpu(iocb->status), le16_to_cpu(ntfy->resp_code)); TRACE_BUFFER("Notify Ack packet data", ntfy, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out: TRACE_EXIT(); @@ -1719,9 +1899,9 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q24_send_abts_resp(scsi_qla_host_t *ha, +static void q24_send_abts_resp(scsi_qla_host_t *vha, const abts24_recv_entry_t *abts, uint32_t status, bool ids_reversed) { abts24_resp_entry_t *resp; @@ -1730,28 +1910,28 @@ static void q24_send_abts_resp(scsi_qla_host_t *ha, TRACE_ENTRY(); - TRACE_DBG("Sending task mgmt ABTS response (ha=%p, atio=%p, " - "status=%x", ha, abts, status); + TRACE_DBG("Sending task mgmt ABTS response (vha=%p, atio=%p, " + "status=%x", vha, abts, status); /* Send marker if required */ - if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + if (q2t_issue_marker(vha, 1) != QLA_SUCCESS) goto out; - resp = (abts24_resp_entry_t *)q2t_req_pkt(ha); + resp = (abts24_resp_entry_t *)q2t_req_pkt(vha); if (resp == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } resp->entry_type = ABTS_RESP_24XX; resp->entry_count = 1; resp->nport_handle = abts->nport_handle; - resp->vp_index = ha->vp_idx; + resp->vp_index = vha->vp_idx; resp->sof_type = abts->sof_type; resp->exchange_address = abts->exchange_address; resp->fcp_hdr_le = abts->fcp_hdr_le; - f_ctl = cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP | + f_ctl = __constant_cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP | F_CTL_LAST_SEQ | F_CTL_END_SEQ | F_CTL_SEQ_INITIATIVE); p = (uint8_t *)&f_ctl; @@ -1790,9 +1970,9 @@ static void q24_send_abts_resp(scsi_qla_host_t *ha, TRACE_BUFFER("ABTS RESP packet data", resp, REQUEST_ENTRY_SIZE); - ha->tgt->abts_resp_expected++; + vha->tgt->abts_resp_expected++; - q2t_exec_queue(ha); + q2t_exec_queue(vha); out: TRACE_EXIT(); @@ -1800,25 +1980,25 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q24_retry_term_exchange(scsi_qla_host_t *ha, +static void q24_retry_term_exchange(scsi_qla_host_t *vha, abts24_resp_fw_entry_t *entry) { ctio7_status1_entry_t *ctio; TRACE_ENTRY(); - TRACE_DBG("Sending retry TERM EXCH CTIO7 (ha=%p)", ha); + TRACE_DBG("Sending retry TERM EXCH CTIO7 (vha=%p)", vha); /* Send marker if required */ - if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + if (q2t_issue_marker(vha, 1) != QLA_SUCCESS) goto out; - ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(vha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } @@ -1831,20 +2011,20 @@ static void q24_retry_term_exchange(scsi_qla_host_t *ha, ctio->common.entry_count = 1; ctio->common.nport_handle = entry->nport_handle; ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; - ctio->common.timeout = cpu_to_le16(Q2T_TIMEOUT); - ctio->common.vp_index = ha->vp_idx; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = vha->vp_idx; ctio->common.initiator_id[0] = entry->fcp_hdr_le.d_id[0]; ctio->common.initiator_id[1] = entry->fcp_hdr_le.d_id[1]; ctio->common.initiator_id[2] = entry->fcp_hdr_le.d_id[2]; ctio->common.exchange_addr = entry->exchange_addr_to_abort; - ctio->flags = cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); + ctio->flags = __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); ctio->ox_id = entry->fcp_hdr_le.ox_id; TRACE_BUFFER("CTIO7 retry TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); - q24_send_abts_resp(ha, (abts24_recv_entry_t *)entry, + q24_send_abts_resp(vha, (abts24_recv_entry_t *)entry, SCST_MGMT_STATUS_SUCCESS, true); out: @@ -1852,23 +2032,40 @@ out: return; } -/* pha->hardware_lock supposed to be held on entry */ -static int __q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts, +/* ha->hardware_lock supposed to be held on entry */ +static int __q24_handle_abts(scsi_qla_host_t *vha, abts24_recv_entry_t *abts, struct q2t_sess *sess) { int res; uint32_t tag = abts->exchange_addr_to_abort; struct q2t_mgmt_cmd *mcmd; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - TRACE_MGMT_DBG("qla2x00t(%ld): task abort (tag=%d)", ha->instance, - tag); + TRACE_MGMT_DBG("qla2x00t(%ld): task abort (tag=%d)", vha->host_no, tag); + + /* + * Clearing all unknown ATIOs, because the aborted one can be + * among them. Let's be simpler and don't implement search of + * the aborted command in the unknown list. It should be harmless + * and on the next retry work well. + */ + while (!list_empty(&ha->unknown_atio_list)) { + struct q2t_unknown_atio *u; + u = list_first_entry(&ha->unknown_atio_list, + struct q2t_unknown_atio, unknown_atio_list_entry); + TRACE_MGMT_DBG("qla2x00t(%ld): Clearing unknown " + "ATIO_TYPE7 %p", vha->host_no, u); + q24_send_term_exchange(vha, NULL, &u->atio7, 1); + list_del(&u->unknown_atio_list_entry); + kfree(u); + } mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); if (mcmd == NULL) { PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed", - ha->instance, __func__); + vha->host_no, __func__); res = -ENOMEM; goto out; } @@ -1881,7 +2078,7 @@ static int __q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts, SCST_ATOMIC, mcmd); if (res != 0) { PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d", - ha->instance, res); + vha->host_no, res); goto out_free; } @@ -1895,9 +2092,9 @@ out_free: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts) +static void q24_handle_abts(scsi_qla_host_t *vha, abts24_recv_entry_t *abts) { int rc; uint32_t tag = abts->exchange_addr_to_abort; @@ -1907,38 +2104,38 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts) if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) { PRINT_ERROR("qla2x00t(%ld): ABTS: Abort Sequence not " - "supported", ha->instance); + "supported", vha->host_no); goto out_err; } if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) { TRACE_MGMT_DBG("qla2x00t(%ld): ABTS: Unknown Exchange " - "Address received", ha->instance); + "Address received", vha->host_no); goto out_err; } TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort (s_id=%x:%x:%x, " - "tag=%d, param=%x)", ha->instance, abts->fcp_hdr_le.s_id[2], + "tag=%d, param=%x)", vha->host_no, abts->fcp_hdr_le.s_id[2], abts->fcp_hdr_le.s_id[1], abts->fcp_hdr_le.s_id[0], tag, le32_to_cpu(abts->fcp_hdr_le.parameter)); - sess = q2t_find_sess_by_s_id_le(ha->tgt, abts->fcp_hdr_le.s_id); + sess = q2t_find_sess_by_s_id_le(vha->tgt, abts->fcp_hdr_le.s_id); if (sess == NULL) { TRACE_MGMT_DBG("qla2x00t(%ld): task abort for unexisting " - "session", ha->instance); - rc = q2t_sched_sess_work(ha->tgt, Q2T_SESS_WORK_ABORT, abts, + "session", vha->host_no); + rc = q2t_sched_sess_work(vha->tgt, Q2T_SESS_WORK_ABORT, abts, sizeof(*abts)); if (rc != 0) { - ha->tgt->tm_to_unknown = 1; + vha->tgt->tm_to_unknown = 1; goto out_err; } goto out; } - rc = __q24_handle_abts(ha, abts, sess); + rc = __q24_handle_abts(vha, abts, sess); if (rc != 0) { PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d", - ha->instance, rc); + vha->host_no, rc); goto out_err; } @@ -1947,14 +2144,14 @@ out: return; out_err: - q24_send_abts_resp(ha, abts, SCST_MGMT_STATUS_REJECTED, false); + q24_send_abts_resp(vha, abts, SCST_MGMT_STATUS_REJECTED, false); goto out; } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha, +static void q24_send_task_mgmt_ctio(scsi_qla_host_t *vha, struct q2t_mgmt_cmd *mcmd, uint32_t resp_code) { const atio7_entry_t *atio = &mcmd->orig_iocb.atio7; @@ -1962,17 +2159,17 @@ static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha, TRACE_ENTRY(); - TRACE_DBG("Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x", - ha, atio, resp_code); + TRACE_DBG("Sending task mgmt CTIO7 (vha=%p, atio=%p, resp_code=%x", + vha, atio, resp_code); /* Send marker if required */ - if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + if (q2t_issue_marker(vha, 1) != QLA_SUCCESS) goto out; - ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(vha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } @@ -1980,22 +2177,22 @@ static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha, ctio->common.entry_count = 1; ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; ctio->common.nport_handle = mcmd->sess->loop_id; - ctio->common.timeout = cpu_to_le16(Q2T_TIMEOUT); - ctio->common.vp_index = ha->vp_idx; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = vha->vp_idx; ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; ctio->common.exchange_addr = atio->exchange_addr; - ctio->flags = (atio->attr << 9) | cpu_to_le16( + ctio->flags = (atio->attr << 9) | __constant_cpu_to_le16( CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS); ctio->ox_id = swab16(atio->fcp_hdr.ox_id); - ctio->scsi_status = cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID); - ctio->response_len = cpu_to_le16(8); + ctio->scsi_status = __constant_cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID); + ctio->response_len = __constant_cpu_to_le16(8); ((uint32_t *)ctio->sense_data)[0] = cpu_to_le32(resp_code); TRACE_BUFFER("CTIO7 TASK MGMT packet data", ctio, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out: TRACE_EXIT(); @@ -2003,9 +2200,9 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q24_send_notify_ack(scsi_qla_host_t *ha, +static void q24_send_notify_ack(scsi_qla_host_t *vha, notify24xx_entry_t *iocb, uint16_t srr_flags, uint8_t srr_reject_code, uint8_t srr_explan) { @@ -2013,19 +2210,19 @@ static void q24_send_notify_ack(scsi_qla_host_t *ha, TRACE_ENTRY(); - TRACE_DBG("Sending NOTIFY_ACK24 (ha=%p)", ha); + TRACE_DBG("Sending NOTIFY_ACK24 (vha=%p)", vha); /* Send marker if required */ - if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + if (q2t_issue_marker(vha, 1) != QLA_SUCCESS) goto out; - if (ha->tgt != NULL) - ha->tgt->notify_ack_expected++; + if (vha->tgt != NULL) + vha->tgt->notify_ack_expected++; - nack = (nack24xx_entry_t *)q2t_req_pkt(ha); + nack = (nack24xx_entry_t *)q2t_req_pkt(vha); if (nack == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } @@ -2034,11 +2231,12 @@ static void q24_send_notify_ack(scsi_qla_host_t *ha, nack->nport_handle = iocb->nport_handle; if (le16_to_cpu(iocb->status) == IMM_NTFY_ELS) { nack->flags = iocb->flags & - cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); + __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); } nack->srr_rx_id = iocb->srr_rx_id; nack->status = iocb->status; nack->status_subcode = iocb->status_subcode; + nack->fw_handle = iocb->fw_handle; nack->exchange_address = iocb->exchange_address; nack->srr_rel_offs = iocb->srr_rel_offs; nack->srr_ui = iocb->srr_ui; @@ -2049,10 +2247,10 @@ static void q24_send_notify_ack(scsi_qla_host_t *ha, nack->vp_index = iocb->vp_index; TRACE(TRACE_SCSI, "qla2x00t(%ld): Sending 24xx Notify Ack %d", - ha->instance, nack->status); + vha->host_no, nack->status); TRACE_BUFFER("24xx Notify Ack packet data", nack, sizeof(*nack)); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out: TRACE_EXIT(); @@ -2090,7 +2288,8 @@ static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) { struct q2t_mgmt_cmd *mcmd; unsigned long flags; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; TRACE_ENTRY(); @@ -2103,31 +2302,31 @@ static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) goto out; } - ha = mcmd->sess->tgt->ha; - pha = to_qla_parent(ha); + vha = mcmd->sess->tgt->vha; + ha = vha->hw; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); if (IS_FWI2_CAPABLE(ha)) { if (mcmd->flags == Q24_MGMT_SEND_NACK) { - q24_send_notify_ack(ha, + q24_send_notify_ack(vha, &mcmd->orig_iocb.notify_entry24, 0, 0, 0); } else { if (scst_mcmd->fn == SCST_ABORT_TASK) - q24_send_abts_resp(ha, &mcmd->orig_iocb.abts, + q24_send_abts_resp(vha, &mcmd->orig_iocb.abts, scst_mgmt_cmd_get_status(scst_mcmd), false); else - q24_send_task_mgmt_ctio(ha, mcmd, + q24_send_task_mgmt_ctio(vha, mcmd, q2t_convert_to_fc_tm_status( scst_mgmt_cmd_get_status(scst_mcmd))); } } else { uint32_t resp_code = q2t_convert_to_fc_tm_status( scst_mgmt_cmd_get_status(scst_mcmd)); - q2x_send_notify_ack(ha, &mcmd->orig_iocb.notify_entry, 0, + q2x_send_notify_ack(vha, &mcmd->orig_iocb.notify_entry, 0, resp_code, 1, 0, 0, 0); } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); mempool_free(mcmd, q2t_mgmt_cmd_mempool); @@ -2145,7 +2344,7 @@ static int q2t_pci_map_calc_cnt(struct q2t_prm *prm) sBUG_ON(prm->cmd->sg_cnt == 0); prm->sg = prm->cmd->sg; - prm->seg_cnt = pci_map_sg(prm->tgt->ha->pdev, prm->cmd->sg, + prm->seg_cnt = pci_map_sg(prm->tgt->vha->hw->pdev, prm->cmd->sg, prm->cmd->sg_cnt, prm->cmd->dma_data_direction); if (unlikely(prm->seg_cnt == 0)) goto out_err; @@ -2167,56 +2366,56 @@ out: out_err: PRINT_ERROR("qla2x00t(%ld): PCI mapping failed: sg_cnt=%d", - prm->tgt->ha->instance, prm->cmd->sg_cnt); + prm->tgt->vha->host_no, prm->cmd->sg_cnt); res = -1; goto out; } -static inline void q2t_unmap_sg(scsi_qla_host_t *ha, struct q2t_cmd *cmd) +static inline void q2t_unmap_sg(scsi_qla_host_t *vha, struct q2t_cmd *cmd) { EXTRACHECKS_BUG_ON(!cmd->sg_mapped); - pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); + pci_unmap_sg(vha->hw->pdev, cmd->sg, cmd->sg_cnt, + cmd->dma_data_direction); cmd->sg_mapped = 0; } -static int q2t_check_reserve_free_req(scsi_qla_host_t *ha, uint32_t req_cnt) +static int q2t_check_reserve_free_req(scsi_qla_host_t *vha, uint32_t req_cnt) { int res = SCST_TGT_RES_SUCCESS; - device_reg_t __iomem *reg; + struct qla_hw_data *ha = vha->hw; + device_reg_t __iomem *reg = ha->iobase; uint32_t cnt; TRACE_ENTRY(); - ha = to_qla_parent(ha); - reg = ha->iobase; - - if (ha->req_q_cnt < (req_cnt + 2)) { + if (vha->req->cnt < (req_cnt + 2)) { if (IS_FWI2_CAPABLE(ha)) - cnt = (uint16_t)RD_REG_DWORD( - ®->isp24.req_q_out); + cnt = (uint16_t)RD_REG_DWORD(®->isp24.req_q_out); else cnt = qla2x00_debounce_register( ISP_REQ_Q_OUT(ha, ®->isp)); - TRACE_DBG("Request ring circled: cnt=%d, " - "ha->req_ring_index=%d, ha->req_q_cnt=%d, req_cnt=%d", - cnt, ha->req_ring_index, ha->req_q_cnt, req_cnt); - if (ha->req_ring_index < cnt) - ha->req_q_cnt = cnt - ha->req_ring_index; - else - ha->req_q_cnt = ha->request_q_length - - (ha->req_ring_index - cnt); - if (unlikely(ha->req_q_cnt < (req_cnt + 2))) { - TRACE(TRACE_OUT_OF_MEM, "qla2x00t(%ld): There is no room in the " - "request ring: ha->req_ring_index=%d, ha->req_q_cnt=%d, " - "req_cnt=%d", ha->instance, ha->req_ring_index, - ha->req_q_cnt, req_cnt); + TRACE_DBG("Request ring circled: cnt=%d, vha->->ring_index=%d, " + "vha->req->cnt=%d, req_cnt=%d\n", cnt, + vha->req->ring_index, vha->req->cnt, req_cnt); + if (vha->req->ring_index < cnt) + vha->req->cnt = cnt - vha->req->ring_index; + else + vha->req->cnt = vha->req->length - + (vha->req->ring_index - cnt); + + if (unlikely(vha->req->cnt < (req_cnt + 2))) { + TRACE(TRACE_OUT_OF_MEM, + "qla_target(%d): There is no room in the " + "request ring: vha->req->ring_index=%d, " + "vha->req->cnt=%d, req_cnt=%d\n", vha->vp_idx, + vha->req->ring_index, vha->req->cnt, req_cnt); res = SCST_TGT_RES_QUEUE_FULL; goto out; } } - ha->req_q_cnt -= req_cnt; + vha->req->cnt -= req_cnt; out: TRACE_EXIT_RES(res); @@ -2224,74 +2423,74 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static inline void *q2t_get_req_pkt(scsi_qla_host_t *ha) +static inline void *q2t_get_req_pkt(scsi_qla_host_t *vha) { - ha = to_qla_parent(ha); - /* Adjust ring index. */ - ha->req_ring_index++; - if (ha->req_ring_index == ha->request_q_length) { - ha->req_ring_index = 0; - ha->request_ring_ptr = ha->request_ring; + vha->req->ring_index++; + if (vha->req->ring_index == vha->req->length) { + vha->req->ring_index = 0; + vha->req->ring_ptr = vha->req->ring; } else { - ha->request_ring_ptr++; + vha->req->ring_ptr++; } - return (cont_entry_t *)ha->request_ring_ptr; + return (cont_entry_t *)vha->req->ring_ptr; } -/* pha->hardware_lock supposed to be held on entry */ -static inline uint32_t q2t_make_handle(scsi_qla_host_t *ha) +/* ha->hardware_lock supposed to be held on entry */ +static inline uint32_t q2t_make_handle(scsi_qla_host_t *vha) { uint32_t h; + struct qla_hw_data *ha = vha->hw; - h = ha->current_handle; + h = vha->current_handle; /* always increment cmd handle */ do { ++h; if (h > MAX_OUTSTANDING_COMMANDS) h = 1; /* 0 is Q2T_NULL_HANDLE */ - if (h == ha->current_handle) { + if (h == vha->current_handle) { TRACE(TRACE_OUT_OF_MEM, "qla2x00t(%ld): Ran out of " - "empty cmd slots in ha %p", ha->instance, ha); + "empty cmd slots in ha %p", vha->host_no, ha); h = Q2T_NULL_HANDLE; break; } } while ((h == Q2T_NULL_HANDLE) || (h == Q2T_SKIP_HANDLE) || - (ha->cmds[h-1] != NULL)); + (vha->cmds[h-1] != NULL)); if (h != Q2T_NULL_HANDLE) - ha->current_handle = h; + vha->current_handle = h; return h; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static void q2x_build_ctio_pkt(struct q2t_prm *prm) { uint32_t h; ctio_entry_t *pkt; - scsi_qla_host_t *ha = prm->tgt->ha; + scsi_qla_host_t *vha = prm->tgt->vha; + struct qla_hw_data *ha = vha->hw; - pkt = (ctio_entry_t *)ha->request_ring_ptr; + pkt = (ctio_entry_t *)vha->req->ring_ptr; prm->pkt = pkt; memset(pkt, 0, sizeof(*pkt)); - if (prm->tgt->tgt_enable_64bit_addr) + if (ha->enable_64bit_addressing) pkt->common.entry_type = CTIO_A64_TYPE; else pkt->common.entry_type = CONTINUE_TGT_IO_TYPE; pkt->common.entry_count = (uint8_t)prm->req_cnt; - h = q2t_make_handle(ha); + h = q2t_make_handle(vha); if (h != Q2T_NULL_HANDLE) - ha->cmds[h-1] = prm->cmd; + vha->cmds[h-1] = prm->cmd; pkt->common.handle = h | CTIO_COMPLETION_HANDLE_MARK; - pkt->common.timeout = cpu_to_le16(Q2T_TIMEOUT); + pkt->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); /* Set initiator ID */ h = GET_TARGET_ID(ha, &prm->cmd->atio.atio2x); @@ -2301,46 +2500,46 @@ static void q2x_build_ctio_pkt(struct q2t_prm *prm) pkt->common.relative_offset = cpu_to_le32(prm->cmd->offset); TRACE(TRACE_DEBUG|TRACE_SCSI, "qla2x00t(%ld): handle(scst_cmd) -> %08x, " - "timeout %d L %#x -> I %#x E %#x", ha->instance, + "timeout %d L %#x -> I %#x E %#x", vha->host_no, pkt->common.handle, Q2T_TIMEOUT, le16_to_cpu(prm->cmd->atio.atio2x.lun), GET_TARGET_ID(ha, &pkt->common), pkt->common.rx_id); } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static int q24_build_ctio_pkt(struct q2t_prm *prm) { uint32_t h; ctio7_status0_entry_t *pkt; - scsi_qla_host_t *ha = prm->tgt->ha; + scsi_qla_host_t *vha = prm->tgt->vha; atio7_entry_t *atio = &prm->cmd->atio.atio7; int res = SCST_TGT_RES_SUCCESS; TRACE_ENTRY(); - pkt = (ctio7_status0_entry_t *)to_qla_parent(ha)->request_ring_ptr; + pkt = (ctio7_status0_entry_t *)vha->req->ring_ptr; prm->pkt = pkt; memset(pkt, 0, sizeof(*pkt)); pkt->common.entry_type = CTIO_TYPE7; pkt->common.entry_count = (uint8_t)prm->req_cnt; - pkt->common.vp_index = ha->vp_idx; + pkt->common.vp_index = vha->vp_idx; - h = q2t_make_handle(ha); + h = q2t_make_handle(vha); if (unlikely(h == Q2T_NULL_HANDLE)) { /* * CTIO type 7 from the firmware doesn't provide a way to - * know the initiator's LOOP ID, hence we can't find + * know the initiator's lood id, hence we can't find * the session and, so, the command. */ res = SCST_TGT_RES_QUEUE_FULL; goto out; } else - ha->cmds[h-1] = prm->cmd; + vha->cmds[h-1] = prm->cmd; pkt->common.handle = h | CTIO_COMPLETION_HANDLE_MARK; pkt->common.nport_handle = cpu_to_le16(prm->cmd->loop_id); - pkt->common.timeout = cpu_to_le16(Q2T_TIMEOUT); + pkt->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); pkt->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; pkt->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; pkt->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; @@ -2351,28 +2550,28 @@ static int q24_build_ctio_pkt(struct q2t_prm *prm) out: TRACE(TRACE_DEBUG|TRACE_SCSI, "qla2x00t(%ld): handle(scst_cmd) -> %08x, " - "timeout %d, ox_id %#x", ha->instance, pkt->common.handle, + "timeout %d, ox_id %#x", vha->host_no, pkt->common.handle, Q2T_TIMEOUT, le16_to_cpu(pkt->ox_id)); TRACE_EXIT_RES(res); return res; } /* - * pha->hardware_lock supposed to be held on entry. We have already made sure + * ha->hardware_lock supposed to be held on entry. We have already made sure * that there is sufficient amount of request entries to not drop it. */ static void q2t_load_cont_data_segments(struct q2t_prm *prm) { int cnt; uint32_t *dword_ptr; - int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; + int enable_64bit_addressing = prm->tgt->vha->hw->enable_64bit_addressing; TRACE_ENTRY(); /* Build continuation packets */ while (prm->seg_cnt > 0) { cont_a64_entry_t *cont_pkt64 = - (cont_a64_entry_t *)q2t_get_req_pkt(prm->tgt->ha); + (cont_a64_entry_t *)q2t_get_req_pkt(prm->tgt->vha); /* * Make sure that from cont_pkt64 none of @@ -2404,10 +2603,8 @@ static void q2t_load_cont_data_segments(struct q2t_prm *prm) dma_addr_t dma_addr = sg_dma_address(prm->sg); *dword_ptr++ = cpu_to_le32(pci_dma_lo32(dma_addr)); - if (enable_64bit_addressing) { - *dword_ptr++ = - cpu_to_le32(pci_dma_hi32(dma_addr)); - } + if (enable_64bit_addressing) + *dword_ptr++ = cpu_to_le32(pci_dma_hi32(dma_addr)); *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg)); TRACE_SG("S/G Segment Cont. phys_addr=%llx:%llx, len=%d", @@ -2427,14 +2624,14 @@ static void q2t_load_cont_data_segments(struct q2t_prm *prm) } /* - * pha->hardware_lock supposed to be held on entry. We have already made sure + * ha->hardware_lock supposed to be held on entry. We have already made sure * that there is sufficient amount of request entries to not drop it. */ static void q2x_load_data_segments(struct q2t_prm *prm) { int cnt; uint32_t *dword_ptr; - int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; + int enable_64bit_addressing = prm->tgt->vha->hw->enable_64bit_addressing; ctio_common_entry_t *pkt = (ctio_common_entry_t *)prm->pkt; TRACE_DBG("iocb->scsi_status=%x, iocb->flags=%x", @@ -2489,14 +2686,14 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. We have already made sure + * ha->hardware_lock supposed to be held on entry. We have already made sure * that there is sufficient amount of request entries to not drop it. */ static void q24_load_data_segments(struct q2t_prm *prm) { int cnt; uint32_t *dword_ptr; - int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; + int enable_64bit_addressing = prm->tgt->vha->hw->enable_64bit_addressing; ctio7_status0_entry_t *pkt = (ctio7_status0_entry_t *)prm->pkt; TRACE_DBG("iocb->scsi_status=%x, iocb->flags=%x", @@ -2523,9 +2720,8 @@ static void q24_load_data_segments(struct q2t_prm *prm) /* If scatter gather */ TRACE_SG("%s", "Building S/G data segments..."); /* Load command entry data segments */ - for (cnt = 0; - (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt; - cnt++, prm->seg_cnt--) { + for (cnt = 0; (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt; + cnt++, prm->seg_cnt--) { dma_addr_t dma_addr = sg_dma_address(prm->sg); *dword_ptr++ = cpu_to_le32(pci_dma_lo32(dma_addr)); @@ -2553,7 +2749,7 @@ static inline int q2t_has_data(struct q2t_cmd *cmd) } /* - * Acquires pha->hardware lock and returns with the lock held when + * Acquires ha->hardware lock and returns with the lock held when * the result == SCST_TGT_RES_SUCCESS. The lock is unlocked if an * error is returned. */ @@ -2562,8 +2758,8 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, { int res; struct q2t_tgt *tgt = cmd->tgt; - scsi_qla_host_t *ha = tgt->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; uint16_t full_req_cnt; struct scst_cmd *scst_cmd = &cmd->scst_cmd; @@ -2572,21 +2768,21 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, if (unlikely(cmd->aborted)) { TRACE_MGMT_DBG("qla2x00t(%ld): terminating exchange " "for aborted cmd=%p (scst_cmd=%p, tag=%d)", - ha->instance, cmd, scst_cmd, cmd->tag); + vha->host_no, cmd, scst_cmd, cmd->tag); cmd->state = Q2T_STATE_ABORTED; scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_ABORTED); if (IS_FWI2_CAPABLE(ha)) - q24_send_term_exchange(ha, cmd, &cmd->atio.atio7, 0); + q24_send_term_exchange(vha, cmd, &cmd->atio.atio7, 0); else - q2x_send_term_exchange(ha, cmd, &cmd->atio.atio2x, 0); + q2x_send_term_exchange(vha, cmd, &cmd->atio.atio2x, 0); /* !! At this point cmd could be already freed !! */ res = Q2T_PRE_XMIT_RESP_CMD_ABORTED; goto out; } - TRACE(TRACE_SCSI, "qla2x00t(%ld): tag=%lld", ha->instance, + TRACE(TRACE_SCSI, "qla2x00t(%ld): tag=%lld", vha->host_no, scst_cmd_get_tag(scst_cmd)); prm->cmd = cmd; @@ -2604,12 +2800,12 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, TRACE_BUFFER("Sense", prm->sense_buffer, prm->sense_buffer_len); /* Send marker if required */ - if (q2t_issue_marker(ha, 0) != QLA_SUCCESS) { + if (q2t_issue_marker(vha, 0) != QLA_SUCCESS) { res = SCST_TGT_RES_FATAL_ERROR; goto out; } - TRACE_DBG("CTIO start: ha(%d)", (int)ha->instance); + TRACE_DBG("CTIO start: vha(%d)", (int)vha->host_no); if ((xmit_type & Q2T_XMIT_DATA) && q2t_has_data(cmd)) { if (q2t_pci_map_calc_cnt(prm) != 0) { @@ -2659,10 +2855,10 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, prm->req_cnt, full_req_cnt, prm->add_status_pkt); /* Acquire ring specific lock */ - spin_lock_irqsave(&pha->hardware_lock, *flags); + spin_lock_irqsave(&ha->hardware_lock, *flags); /* Does F/W have an IOCBs for this request */ - res = q2t_check_reserve_free_req(ha, full_req_cnt); + res = q2t_check_reserve_free_req(vha, full_req_cnt); /* The following check must match the callers' assumptions */ if (unlikely(res != SCST_TGT_RES_SUCCESS)) @@ -2674,10 +2870,10 @@ out: out_unlock_free_unmap: if (cmd->sg_mapped) - q2t_unmap_sg(ha, cmd); + q2t_unmap_sg(vha, cmd); /* Release ring specific lock */ - spin_unlock_irqrestore(&pha->hardware_lock, *flags); + spin_unlock_irqrestore(&ha->hardware_lock, *flags); goto out; } @@ -2706,16 +2902,18 @@ static void q24_copy_sense_buffer_to_ctio(ctio7_status1_entry_t *ctio, *dst++ = cpu_to_be32(*src); } -static inline int q2t_need_explicit_conf(scsi_qla_host_t *ha, +static inline int q2t_need_explicit_conf(scsi_qla_host_t *vha, struct q2t_cmd *cmd, int sending_sense) { + struct qla_hw_data *ha = vha->hw; + if (ha->enable_class_2) return 0; if (sending_sense) return cmd->conf_compl_supported; else - return ha->enable_explicit_conf && cmd->conf_compl_supported; + return vha->hw->enable_explicit_conf && cmd->conf_compl_supported; } static void q2x_init_ctio_ret_entry(ctio_ret_entry_t *ctio_m1, @@ -2726,18 +2924,18 @@ static void q2x_init_ctio_ret_entry(ctio_ret_entry_t *ctio_m1, prm->sense_buffer_len = min_t(uint32_t, prm->sense_buffer_len, sizeof(ctio_m1->sense_data)); - ctio_m1->flags = cpu_to_le16(OF_SSTS | OF_FAST_POST | + ctio_m1->flags = __constant_cpu_to_le16(OF_SSTS | OF_FAST_POST | OF_NO_DATA | OF_SS_MODE_1); - ctio_m1->flags |= cpu_to_le16(OF_INC_RC); - if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) - ctio_m1->flags |= cpu_to_le16(OF_EXPL_CONF | OF_CONF_REQ); + ctio_m1->flags |= __constant_cpu_to_le16(OF_INC_RC); + if (q2t_need_explicit_conf(prm->tgt->vha, prm->cmd, 0)) + ctio_m1->flags |= __constant_cpu_to_le16(OF_EXPL_CONF | OF_CONF_REQ); ctio_m1->scsi_status = cpu_to_le16(prm->rq_result); ctio_m1->residual = cpu_to_le32(prm->residual); if (scst_sense_valid(prm->sense_buffer)) { - if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) - ctio_m1->flags |= cpu_to_le16(OF_EXPL_CONF | OF_CONF_REQ); - ctio_m1->scsi_status |= cpu_to_le16(SS_SENSE_LEN_VALID); + if (q2t_need_explicit_conf(prm->tgt->vha, prm->cmd, 1)) + ctio_m1->flags |= __constant_cpu_to_le16(OF_EXPL_CONF | OF_CONF_REQ); + ctio_m1->scsi_status |= __constant_cpu_to_le16(SS_SENSE_LEN_VALID); ctio_m1->sense_length = cpu_to_le16(prm->sense_buffer_len); memcpy(ctio_m1->sense_data, prm->sense_buffer, prm->sense_buffer_len); @@ -2756,7 +2954,8 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) { int res; unsigned long flags; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; struct q2t_prm prm; ctio_common_entry_t *pkt; @@ -2771,17 +2970,17 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) goto out; } - /* Here pha->hardware_lock already locked */ + /* Here ha->hardware_lock already locked */ - ha = prm.tgt->ha; - pha = to_qla_parent(ha); + vha = prm.tgt->vha; + ha = vha->hw; q2x_build_ctio_pkt(&prm); pkt = (ctio_common_entry_t *)prm.pkt; if (q2t_has_data(cmd) && (xmit_type & Q2T_XMIT_DATA)) { - pkt->flags |= cpu_to_le16(OF_FAST_POST | OF_DATA_IN); - pkt->flags |= cpu_to_le16(OF_INC_RC); + pkt->flags |= __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_IN); + pkt->flags |= __constant_cpu_to_le16(OF_INC_RC); q2x_load_data_segments(&prm); @@ -2789,9 +2988,9 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) if (xmit_type & Q2T_XMIT_STATUS) { pkt->scsi_status = cpu_to_le16(prm.rq_result); pkt->residual = cpu_to_le32(prm.residual); - pkt->flags |= cpu_to_le16(OF_SSTS); - if (q2t_need_explicit_conf(ha, cmd, 0)) { - pkt->flags |= cpu_to_le16( + pkt->flags |= __constant_cpu_to_le16(OF_SSTS); + if (q2t_need_explicit_conf(vha, cmd, 0)) { + pkt->flags |= __constant_cpu_to_le16( OF_EXPL_CONF | OF_CONF_REQ); } @@ -2803,7 +3002,7 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) * req_pkt(). */ ctio_ret_entry_t *ctio_m1 = - (ctio_ret_entry_t *)q2t_get_req_pkt(ha); + (ctio_ret_entry_t *)q2t_get_req_pkt(vha); TRACE_DBG("%s", "Building additional status packet"); @@ -2813,7 +3012,7 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) /* Real finish is ctio_m1's finish */ pkt->handle |= CTIO_INTERMEDIATE_HANDLE_MARK; - pkt->flags &= ~cpu_to_le16(OF_INC_RC); + pkt->flags &= ~__constant_cpu_to_le16(OF_INC_RC); q2x_init_ctio_ret_entry(ctio_m1, &prm); TRACE_BUFFER("Status CTIO packet data", ctio_m1, @@ -2826,10 +3025,10 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) TRACE_BUFFER("Xmitting", pkt, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); /* Release ring specific lock */ - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); out: TRACE_EXIT_RES(res); @@ -2915,7 +3114,7 @@ static int q2x_xmit_response(struct scst_cmd *scst_cmd) "cmd->data_direction=%d", is_send_status, cmd->bufflen, cmd->sg_cnt, cmd->data_direction); - if (IS_FWI2_CAPABLE(cmd->tgt->ha)) + if (IS_FWI2_CAPABLE(cmd->tgt->vha->hw)) res = __q24_xmit_response(cmd, xmit_type); else res = __q2x_xmit_response(cmd, xmit_type); @@ -2932,9 +3131,9 @@ static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio, prm->sense_buffer_len = min_t(uint32_t, prm->sense_buffer_len, sizeof(ctio1->sense_data)); - ctio->flags |= cpu_to_le16(CTIO7_FLAGS_SEND_STATUS); - if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) { - ctio->flags |= cpu_to_le16( + ctio->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_SEND_STATUS); + if (q2t_need_explicit_conf(prm->tgt->vha, prm->cmd, 0)) { + ctio->flags |= __constant_cpu_to_le16( CTIO7_FLAGS_EXPLICIT_CONFORM | CTIO7_FLAGS_CONFORM_REQ); } @@ -2942,21 +3141,21 @@ static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio, ctio->scsi_status = cpu_to_le16(prm->rq_result); if (scst_sense_valid(prm->sense_buffer)) { ctio1 = (ctio7_status1_entry_t *)ctio; - if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) { - ctio1->flags |= cpu_to_le16( + if (q2t_need_explicit_conf(prm->tgt->vha, prm->cmd, 1)) { + ctio1->flags |= __constant_cpu_to_le16( CTIO7_FLAGS_EXPLICIT_CONFORM | CTIO7_FLAGS_CONFORM_REQ); } - ctio1->flags &= ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); - ctio1->flags |= cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); - ctio1->scsi_status |= cpu_to_le16(SS_SENSE_LEN_VALID); + ctio1->flags &= ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ctio1->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + ctio1->scsi_status |= __constant_cpu_to_le16(SS_SENSE_LEN_VALID); ctio1->sense_length = cpu_to_le16(prm->sense_buffer_len); q24_copy_sense_buffer_to_ctio(ctio1, prm->sense_buffer, prm->sense_buffer_len); } else { ctio1 = (ctio7_status1_entry_t *)ctio; - ctio1->flags &= ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); - ctio1->flags |= cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + ctio1->flags &= ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ctio1->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); ctio1->sense_length = 0; memset(ctio1->sense_data, 0, sizeof(ctio1->sense_data)); } @@ -2971,7 +3170,8 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) { int res; unsigned long flags; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; struct q2t_prm prm; ctio7_status0_entry_t *pkt; @@ -2986,10 +3186,10 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) goto out; } - /* Here pha->hardware_lock already locked */ + /* Here ha->hardware_lock already locked */ - ha = prm.tgt->ha; - pha = to_qla_parent(ha); + vha = prm.tgt->vha; + ha = vha->hw; res = q24_build_ctio_pkt(&prm); if (unlikely(res != SCST_TGT_RES_SUCCESS)) @@ -2998,8 +3198,8 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) pkt = (ctio7_status0_entry_t *)prm.pkt; if (q2t_has_data(cmd) && (xmit_type & Q2T_XMIT_DATA)) { - pkt->flags |= cpu_to_le16(CTIO7_FLAGS_DATA_IN | - CTIO7_FLAGS_STATUS_MODE_0); + pkt->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN | + CTIO7_FLAGS_STATUS_MODE_0); q24_load_data_segments(&prm); @@ -3007,10 +3207,10 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) if (xmit_type & Q2T_XMIT_STATUS) { pkt->scsi_status = cpu_to_le16(prm.rq_result); pkt->residual = cpu_to_le32(prm.residual); - pkt->flags |= cpu_to_le16( + pkt->flags |= __constant_cpu_to_le16( CTIO7_FLAGS_SEND_STATUS); - if (q2t_need_explicit_conf(ha, cmd, 0)) { - pkt->flags |= cpu_to_le16( + if (q2t_need_explicit_conf(vha, cmd, 0)) { + pkt->flags |= __constant_cpu_to_le16( CTIO7_FLAGS_EXPLICIT_CONFORM | CTIO7_FLAGS_CONFORM_REQ); } @@ -3022,18 +3222,20 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) * req_pkt(). */ ctio7_status1_entry_t *ctio = - (ctio7_status1_entry_t *)q2t_get_req_pkt(ha); + (ctio7_status1_entry_t *)q2t_get_req_pkt(vha); TRACE_DBG("%s", "Building additional status packet"); memcpy(ctio, pkt, sizeof(*ctio)); ctio->common.entry_count = 1; ctio->common.dseg_count = 0; - ctio->flags &= ~cpu_to_le16(CTIO7_FLAGS_DATA_IN); + ctio->flags &= ~__constant_cpu_to_le16( + CTIO7_FLAGS_DATA_IN); /* Real finish is ctio_m1's finish */ pkt->common.handle |= CTIO_INTERMEDIATE_HANDLE_MARK; - pkt->flags |= cpu_to_le16(CTIO7_FLAGS_DONT_RET_CTIO); + pkt->flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_DONT_RET_CTIO); q24_init_ctio_ret_entry((ctio7_status0_entry_t *)ctio, &prm); TRACE_BUFFER("Status CTIO7", ctio, REQUEST_ENTRY_SIZE); @@ -3045,11 +3247,11 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) TRACE_BUFFER("Xmitting CTIO7", pkt, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out_unlock: /* Release ring specific lock */ - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); out: TRACE_EXIT_RES(res); @@ -3057,7 +3259,7 @@ out: out_unmap_unlock: if (cmd->sg_mapped) - q2t_unmap_sg(ha, cmd); + q2t_unmap_sg(vha, cmd); goto out_unlock; } @@ -3065,7 +3267,8 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) { int res = SCST_TGT_RES_SUCCESS; unsigned long flags; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; struct q2t_tgt *tgt = cmd->tgt; struct q2t_prm prm; void *p; @@ -3077,16 +3280,16 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) prm.tgt = tgt; prm.sg = NULL; prm.req_cnt = 1; - ha = tgt->ha; - pha = to_qla_parent(ha); + vha = tgt->vha; + ha = vha->hw; /* Send marker if required */ - if (q2t_issue_marker(ha, 0) != QLA_SUCCESS) { + if (q2t_issue_marker(vha, 0) != QLA_SUCCESS) { res = SCST_TGT_RES_FATAL_ERROR; goto out; } - TRACE_DBG("CTIO_start: ha(%d)", (int)ha->instance); + TRACE_DBG("CTIO_start: vha(%d)", (int)vha->host_no); /* Calculate number of entries and segments required */ if (q2t_pci_map_calc_cnt(&prm) != 0) { @@ -3095,10 +3298,10 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) } /* Acquire ring specific lock */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); /* Does F/W have an IOCBs for this request */ - res = q2t_check_reserve_free_req(ha, prm.req_cnt); + res = q2t_check_reserve_free_req(vha, prm.req_cnt); if (res != SCST_TGT_RES_SUCCESS) goto out_unlock_free_unmap; @@ -3108,15 +3311,15 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) if (unlikely(res != SCST_TGT_RES_SUCCESS)) goto out_unlock_free_unmap; pkt = (ctio7_status0_entry_t *)prm.pkt; - pkt->flags |= cpu_to_le16(CTIO7_FLAGS_DATA_OUT | - CTIO7_FLAGS_STATUS_MODE_0); + pkt->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT | + CTIO7_FLAGS_STATUS_MODE_0); q24_load_data_segments(&prm); p = pkt; } else { ctio_common_entry_t *pkt; q2x_build_ctio_pkt(&prm); pkt = (ctio_common_entry_t *)prm.pkt; - pkt->flags = cpu_to_le16(OF_FAST_POST | OF_DATA_OUT); + pkt->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_OUT); q2x_load_data_segments(&prm); p = pkt; } @@ -3125,11 +3328,11 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) TRACE_BUFFER("Xfering", p, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out_unlock: /* Release ring specific lock */ - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); out: TRACE_EXIT_RES(res); @@ -3137,7 +3340,7 @@ out: out_unlock_free_unmap: if (cmd->sg_mapped) - q2t_unmap_sg(ha, cmd); + q2t_unmap_sg(vha, cmd); goto out_unlock; } @@ -3163,29 +3366,29 @@ static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd) } /* If hardware_lock held on entry, might drop it, then reacquire */ -static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, +static void q2x_send_term_exchange(scsi_qla_host_t *vha, struct q2t_cmd *cmd, atio_entry_t *atio, int ha_locked) { ctio_ret_entry_t *ctio; unsigned long flags = 0; /* to stop compiler's warning */ int do_tgt_cmd_done = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - TRACE_DBG("Sending TERM EXCH CTIO (ha=%p)", ha); + TRACE_DBG("Sending TERM EXCH CTIO (vha=%p)", vha); /* Send marker if required */ - if (q2t_issue_marker(ha, ha_locked) != QLA_SUCCESS) + if (q2t_issue_marker(vha, ha_locked) != QLA_SUCCESS) goto out; if (!ha_locked) - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); - ctio = (ctio_ret_entry_t *)q2t_req_pkt(ha); + ctio = (ctio_ret_entry_t *)q2t_req_pkt(vha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out_unlock; } @@ -3194,7 +3397,7 @@ static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, if (cmd != NULL) { if (cmd->state < Q2T_STATE_PROCESSED) { PRINT_ERROR("qla2x00t(%ld): Terminating cmd %p with " - "incorrect state %d", ha->instance, cmd, + "incorrect state %d", vha->host_no, cmd, cmd->state); } else do_tgt_cmd_done = 1; @@ -3210,17 +3413,17 @@ static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, if (ctio->residual != 0) ctio->scsi_status |= SS_RESIDUAL_UNDER; - ctio->flags = cpu_to_le16(OF_FAST_POST | OF_TERM_EXCH | - OF_NO_DATA | OF_SS_MODE_1); - ctio->flags |= cpu_to_le16(OF_INC_RC); + ctio->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_TERM_EXCH | + OF_NO_DATA | OF_SS_MODE_1); + ctio->flags |= __constant_cpu_to_le16(OF_INC_RC); TRACE_BUFFER("CTIO TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out_unlock: if (!ha_locked) - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); if (do_tgt_cmd_done) { if (!ha_locked && !in_interrupt()) @@ -3236,29 +3439,29 @@ out: } /* If hardware_lock held on entry, might drop it, then reacquire */ -static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, +static void q24_send_term_exchange(scsi_qla_host_t *vha, struct q2t_cmd *cmd, atio7_entry_t *atio, int ha_locked) { ctio7_status1_entry_t *ctio; unsigned long flags = 0; /* to stop compiler's warning */ int do_tgt_cmd_done = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - TRACE_DBG("Sending TERM EXCH CTIO7 (ha=%p)", ha); + TRACE_DBG("Sending TERM EXCH CTIO7 (vha=%p)", vha); /* Send marker if required */ - if (q2t_issue_marker(ha, ha_locked) != QLA_SUCCESS) + if (q2t_issue_marker(vha, ha_locked) != QLA_SUCCESS) goto out; if (!ha_locked) - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); - ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(vha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out_unlock; } @@ -3268,20 +3471,20 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, ctio->common.nport_handle = cmd->loop_id; if (cmd->state < Q2T_STATE_PROCESSED) { PRINT_ERROR("qla2x00t(%ld): Terminating cmd %p with " - "incorrect state %d", ha->instance, cmd, + "incorrect state %d", vha->host_no, cmd, cmd->state); } else do_tgt_cmd_done = 1; } else ctio->common.nport_handle = CTIO7_NHANDLE_UNRECOGNIZED; ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; - ctio->common.timeout = cpu_to_le16(Q2T_TIMEOUT); - ctio->common.vp_index = ha->vp_idx; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = vha->vp_idx; ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; ctio->common.exchange_addr = atio->exchange_addr; - ctio->flags = (atio->attr << 9) | cpu_to_le16( + ctio->flags = (atio->attr << 9) | __constant_cpu_to_le16( CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); ctio->ox_id = swab16(atio->fcp_hdr.ox_id); @@ -3293,11 +3496,11 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, TRACE_BUFFER("CTIO7 TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out_unlock: if (!ha_locked) - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); if (do_tgt_cmd_done) { if (!ha_locked && !in_interrupt()) @@ -3338,23 +3541,23 @@ static void q2t_on_free_cmd(struct scst_cmd *scst_cmd) return; } -/* pha->hardware_lock supposed to be held on entry */ -static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd, +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_prepare_srr_ctio(scsi_qla_host_t *vha, struct q2t_cmd *cmd, void *ctio) { struct srr_ctio *sc; - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; int res = 0; struct srr_imm *imm; tgt->ctio_srr_id++; TRACE_MGMT_DBG("qla2x00t(%ld): CTIO with SRR " - "status received", ha->instance); + "status received", vha->host_no); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): SRR CTIO, " - "but ctio is NULL", ha->instance); + "but ctio is NULL", vha->host_no); res = -EINVAL; goto out; } @@ -3387,7 +3590,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd, PRINT_ERROR("qla2x00t(%ld): imm_srr_id " "== ctio_srr_id (%d), but there is no " "corresponding SRR IMM, deleting CTIO " - "SRR %p", ha->instance, tgt->ctio_srr_id, + "SRR %p", vha->host_no, tgt->ctio_srr_id, sc); list_del(&sc->srr_list_entry); spin_unlock(&tgt->srr_lock); @@ -3401,7 +3604,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd, } else { struct srr_imm *ti; PRINT_ERROR("qla2x00t(%ld): Unable to allocate SRR CTIO entry", - ha->instance); + vha->host_no); spin_lock(&tgt->srr_lock); list_for_each_entry_safe(imm, ti, &tgt->srr_imm_list, srr_list_entry) { @@ -3409,7 +3612,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd, TRACE_MGMT_DBG("IMM SRR %p deleted " "(id %d)", imm, imm->srr_id); list_del(&imm->srr_list_entry); - q2t_reject_free_srr_imm(ha, imm, 1); + q2t_reject_free_srr_imm(vha, imm, 1); } } spin_unlock(&tgt->srr_lock); @@ -3423,45 +3626,42 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static bool q2t_term_ctio_exchange(scsi_qla_host_t *ha, void *ctio, +static bool q2t_term_ctio_exchange(scsi_qla_host_t *vha, void *ctio, struct q2t_cmd *cmd, uint32_t status) { bool term = false; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); if (IS_FWI2_CAPABLE(ha)) { if (cmd == NULL) { /* - * We can't get loop ID from CTIO7 and + * We can't get loop id from CTIO7 and * ATIO7 from NULL cmd. */ goto out; } if (ctio != NULL) { ctio7_fw_entry_t *c = (ctio7_fw_entry_t *)ctio; - term = !(c->flags & cpu_to_le16(OF_TERM_EXCH)); + term = !(c->flags & __constant_cpu_to_le16(OF_TERM_EXCH)); } else term = true; - if (term) { - q24_send_term_exchange(ha, cmd, - &cmd->atio.atio7, 1); - } + if (term) + q24_send_term_exchange(vha, cmd, &cmd->atio.atio7, 1); } else { if (status != CTIO_SUCCESS) - q2x_modify_command_count(ha, 1, 0); + q2x_modify_command_count(vha, 1, 0); #if 0 /* Seems, it isn't needed. If enable it, add support for NULL cmd! */ if (ctio != NULL) { ctio_common_entry_t *c = (ctio_common_entry_t *)ctio; - term = !(c->flags & cpu_to_le16(CTIO7_FLAGS_TERMINATE)); + term = !(c->flags & __constant_cpu_to_le16(CTIO7_FLAGS_TERMINATE)); } else term = true; - if (term) { - q2x_send_term_exchange(ha, cmd, - &cmd->atio.atio2x, 1); - } + if (term) + q2x_send_term_exchange(ha, cmd, &cmd->atio.atio2x, 1); #endif } @@ -3470,43 +3670,51 @@ out: return term; } -/* pha->hardware_lock supposed to be held on entry */ -static inline struct q2t_cmd *q2t_get_cmd(scsi_qla_host_t *ha, uint32_t handle) +/* ha->hardware_lock supposed to be held on entry */ +static inline struct q2t_cmd *q2t_get_cmd(scsi_qla_host_t *vha, uint32_t handle) { handle--; - if (ha->cmds[handle] != NULL) { - struct q2t_cmd *cmd = ha->cmds[handle]; - ha->cmds[handle] = NULL; + if (vha->cmds[handle] != NULL) { + struct q2t_cmd *cmd = vha->cmds[handle]; + vha->cmds[handle] = NULL; return cmd; } else return NULL; } -/* pha->hardware_lock supposed to be held on entry */ -static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle, +/* ha->hardware_lock supposed to be held on entry */ +static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *vha, uint32_t handle, void *ctio) { struct q2t_cmd *cmd = NULL; + struct qla_hw_data *ha = vha->hw; /* Clear out internal marks */ handle &= ~(CTIO_COMPLETION_HANDLE_MARK | CTIO_INTERMEDIATE_HANDLE_MARK); if (handle != Q2T_NULL_HANDLE) { +#ifndef __COVERITY__ + /* + * Although the code below is never reached we want to keep it. + * The #ifndef __COVERITY__ construct avoids that Coveritiy + * reports that the code below is dead. + */ if (unlikely(handle == Q2T_SKIP_HANDLE)) { TRACE_DBG("%s", "SKIP_HANDLE CTIO"); goto out; } +#endif /* handle-1 is actually used */ if (unlikely(handle > MAX_OUTSTANDING_COMMANDS)) { PRINT_ERROR("qla2x00t(%ld): Wrong handle %x " - "received", ha->instance, handle); + "received", vha->host_no, handle); goto out; } - cmd = q2t_get_cmd(ha, handle); + cmd = q2t_get_cmd(vha, handle); if (unlikely(cmd == NULL)) { PRINT_WARNING("qla2x00t(%ld): Suspicious: unable to " "find the command with handle %x", - ha->instance, handle); + vha->host_no, handle); goto out; } } else if (ctio != NULL) { @@ -3516,10 +3724,10 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle, struct scst_cmd *scst_cmd; if (IS_FWI2_CAPABLE(ha)) { - /* We can't get loop ID from CTIO7 */ + /* We can't get loop id from CTIO7 */ PRINT_ERROR("qla2x00t(%ld): Wrong CTIO received: " "QLA24xx doesn't support NULL handles", - ha->instance); + vha->host_no); goto out; } else { ctio_common_entry_t *c = (ctio_common_entry_t *)ctio; @@ -3527,12 +3735,12 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle, tag = c->rx_id; } - sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); + sess = q2t_find_sess_by_loop_id(vha->tgt, loop_id); if (sess == NULL) { PRINT_WARNING("qla2x00t(%ld): Suspicious: " "ctio_completion for non-existing session " "(loop_id %d, tag %d)", - ha->instance, loop_id, tag); + vha->host_no, loop_id, tag); goto out; } @@ -3540,7 +3748,7 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle, if (scst_cmd == NULL) { PRINT_WARNING("qla2x00t(%ld): Suspicious: unable to " "find the command with tag %d (loop_id %d)", - ha->instance, tag, loop_id); + vha->host_no, tag, loop_id); goto out; } @@ -3553,9 +3761,9 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, +static void q2t_do_ctio_completion(scsi_qla_host_t *vha, uint32_t handle, uint32_t status, void *ctio) { struct scst_cmd *scst_cmd; @@ -3571,7 +3779,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, #endif TRACE(TRACE_DEBUG|TRACE_SCSI, "qla2x00t(%ld): handle(ctio %p " - "status %#x) <- %08x", ha->instance, ctio, status, handle); + "status %#x) <- %08x", vha->host_no, ctio, status, handle); if (handle & CTIO_INTERMEDIATE_HANDLE_MARK) { /* That could happen only in case of an error/reset/abort */ @@ -3582,17 +3790,17 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, goto out; } - cmd = q2t_ctio_to_cmd(ha, handle, ctio); + cmd = q2t_ctio_to_cmd(vha, handle, ctio); if (cmd == NULL) { if (status != CTIO_SUCCESS) - q2t_term_ctio_exchange(ha, ctio, NULL, status); + q2t_term_ctio_exchange(vha, ctio, NULL, status); goto out; } scst_cmd = &cmd->scst_cmd; if (cmd->sg_mapped) - q2t_unmap_sg(ha, cmd); + q2t_unmap_sg(vha, cmd); if (unlikely(status != CTIO_SUCCESS)) { switch (status & 0xFFFF) { @@ -3606,7 +3814,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, "qla2x00t(%ld): CTIO with " "status %#x received, state %x, scst_cmd %p, " "op %s (LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " - "TIMEOUT=b, INVALID_RX_ID=8)", ha->instance, + "TIMEOUT=b, INVALID_RX_ID=8)", vha->host_no, status, cmd->state, scst_cmd, scst_get_opcode_name(scst_cmd)); break; @@ -3615,12 +3823,12 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, PRINT_INFO("qla2x00t(%ld): CTIO with PORT LOGGED " "OUT (29) or PORT UNAVAILABLE (28) status %x " "received (state %x, scst_cmd %p, op %s)", - ha->instance, status, cmd->state, scst_cmd, + vha->host_no, status, cmd->state, scst_cmd, scst_get_opcode_name(scst_cmd)); break; case CTIO_SRR_RECEIVED: - if (q2t_prepare_srr_ctio(ha, cmd, ctio) != 0) + if (q2t_prepare_srr_ctio(vha, cmd, ctio) != 0) break; else goto out; @@ -3628,13 +3836,13 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, default: PRINT_ERROR("qla2x00t(%ld): CTIO with error status " "0x%x received (state %x, scst_cmd %p, op %s)", - ha->instance, status, cmd->state, scst_cmd, + vha->host_no, status, cmd->state, scst_cmd, scst_get_opcode_name(scst_cmd)); break; } if (cmd->state != Q2T_STATE_NEED_DATA) - if (q2t_term_ctio_exchange(ha, ctio, cmd, status)) + if (q2t_term_ctio_exchange(vha, ctio, cmd, status)) goto out; } @@ -3662,7 +3870,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, cmd->tag); } else { PRINT_ERROR("qla2x00t(%ld): A command in state (%d) should " - "not return a CTIO complete", ha->instance, cmd->state); + "not return a CTIO complete", vha->host_no, cmd->state); } if (unlikely(status != CTIO_SUCCESS)) { @@ -3677,28 +3885,28 @@ out: return; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ /* called via callback from qla2xxx */ -static void q2x_ctio_completion(scsi_qla_host_t *ha, uint32_t handle) +static void q2x_ctio_completion(scsi_qla_host_t *vha, uint32_t handle) { - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; TRACE_ENTRY(); if (likely(tgt != NULL)) { tgt->irq_cmd_count++; - q2t_do_ctio_completion(ha, handle, CTIO_SUCCESS, NULL); + q2t_do_ctio_completion(vha, handle, CTIO_SUCCESS, NULL); tgt->irq_cmd_count--; } else { - TRACE_DBG("CTIO, but target mode not enabled (ha %p handle " - "%#x)", ha, handle); + TRACE_DBG("CTIO, but target mode not enabled (vha %p handle " + "%#x)", vha, handle); } TRACE_EXIT(); return; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd) { int res; @@ -3772,7 +3980,7 @@ out: return res; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd) { int res; @@ -3802,8 +4010,8 @@ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd) else dir = SCST_DATA_NONE; scst_cmd_set_expected(&cmd->scst_cmd, dir, - get_unaligned_be32( - &atio->fcp_cmnd.add_cdb[atio->fcp_cmnd.add_cdb_len])); + be32_to_cpu(get_unaligned((uint32_t *)&atio->fcp_cmnd.add_cdb[ + atio->fcp_cmnd.add_cdb_len]))); switch (atio->fcp_cmnd.task_attr) { case ATIO_SIMPLE_QUEUE: @@ -3844,11 +4052,12 @@ out: return res; } -/* pha->hardware_lock supposed to be held on entry */ -static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha, +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *vha, struct q2t_cmd *cmd, struct q2t_sess *sess) { int res; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); @@ -3865,13 +4074,14 @@ static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha, return res; } -/* pha->hardware_lock supposed to be held on entry */ -static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio) +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_send_cmd_to_scst(scsi_qla_host_t *vha, atio_t *atio) { int res = 0; - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; struct q2t_sess *sess; struct q2t_cmd *cmd; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); @@ -3885,14 +4095,14 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio) cmd = kmem_cache_zalloc(q2t_cmd_cachep, GFP_ATOMIC); if (cmd == NULL) { TRACE(TRACE_OUT_OF_MEM, "qla2x00t(%ld): Allocation of cmd " - "failed", ha->instance); + "failed", vha->host_no); res = -ENOMEM; goto out; } memcpy(&cmd->atio.atio2x, atio, sizeof(*atio)); cmd->state = Q2T_STATE_NEW; - cmd->tgt = ha->tgt; + cmd->tgt = vha->tgt; if (IS_FWI2_CAPABLE(ha)) { atio7_entry_t *a = (atio7_entry_t *)atio; @@ -3900,7 +4110,7 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio) if (unlikely(sess == NULL)) { TRACE_MGMT_DBG("qla2x00t(%ld): Unable to find " "wwn login (s_id %x:%x:%x), trying to create " - "it manually", ha->instance, + "it manually", vha->host_no, a->fcp_hdr.s_id[0], a->fcp_hdr.s_id[1], a->fcp_hdr.s_id[2]); goto out_sched; @@ -3911,13 +4121,13 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio) if (unlikely(sess == NULL)) { TRACE_MGMT_DBG("qla2x00t(%ld): Unable to find " "wwn login (loop_id=%d), trying to create it " - "manually", ha->instance, + "manually", vha->host_no, GET_TARGET_ID(ha, (atio_entry_t *)atio)); goto out_sched; } } - res = q2t_do_send_cmd_to_scst(ha, cmd, sess); + res = q2t_do_send_cmd_to_scst(vha, cmd, sess); if (unlikely(res != 0)) goto out_free_cmd; @@ -3941,20 +4151,40 @@ out_sched: goto out; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun, int lun_size, int fn, void *iocb, int flags) { int res = 0, rc = -1; struct q2t_mgmt_cmd *mcmd; + struct qla_hw_data *ha = sess->tgt->vha->hw; TRACE_ENTRY(); + /* + * Clearing all unknown ATIOs, because the aborted ones can be + * among them. Let's be simpler and don't implement search of + * the aborted commands in the unknown list. It should be harmless + * and on the next retry work well. + */ + while (!list_empty(&ha->unknown_atio_list)) { + struct q2t_unknown_atio *u; + u = list_first_entry(&ha->unknown_atio_list, + struct q2t_unknown_atio, unknown_atio_list_entry); + TRACE_MGMT_DBG("qla2x00t(%ld): Clearing unknown " + "ATIO_TYPE7 %p", u->vha->host_no, u); + WARN_ON(!IS_FWI2_CAPABLE(u->vha->hw)); + BUG_ON(u->vha->hw != ha); + q24_send_term_exchange(u->vha, NULL, &u->atio7, 1); + list_del(&u->unknown_atio_list_entry); + kfree(u); + } + mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); if (mcmd == NULL) { PRINT_CRIT_ERROR("qla2x00t(%ld): Allocation of management " "command failed, some commands and their data could " - "leak", sess->tgt->ha->instance); + "leak", sess->tgt->vha->host_no); res = -ENOMEM; goto out; } @@ -3970,42 +4200,42 @@ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun, switch (fn) { case Q2T_CLEAR_ACA: TRACE(TRACE_MGMT, "qla2x00t(%ld): CLEAR_ACA received", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_ACA, lun, lun_size, SCST_ATOMIC, mcmd); break; case Q2T_TARGET_RESET: TRACE(TRACE_MGMT, "qla2x00t(%ld): TARGET_RESET received", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_TARGET_RESET, lun, lun_size, SCST_ATOMIC, mcmd); break; case Q2T_LUN_RESET: TRACE(TRACE_MGMT, "qla2x00t(%ld): LUN_RESET received", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_LUN_RESET, lun, lun_size, SCST_ATOMIC, mcmd); break; case Q2T_CLEAR_TS: TRACE(TRACE_MGMT, "qla2x00t(%ld): CLEAR_TS received", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_TASK_SET, lun, lun_size, SCST_ATOMIC, mcmd); break; case Q2T_ABORT_TS: TRACE(TRACE_MGMT, "qla2x00t(%ld): ABORT_TS received", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_TASK_SET, lun, lun_size, SCST_ATOMIC, mcmd); break; case Q2T_ABORT_ALL: TRACE(TRACE_MGMT, "qla2x00t(%ld): Doing ABORT_ALL_TASKS", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_ALL_TASKS, lun, lun_size, SCST_ATOMIC, mcmd); @@ -4013,7 +4243,7 @@ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun, case Q2T_ABORT_ALL_SESS: TRACE(TRACE_MGMT, "qla2x00t(%ld): Doing ABORT_ALL_TASKS_SESS", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_ALL_TASKS_SESS, lun, lun_size, SCST_ATOMIC, mcmd); @@ -4021,28 +4251,28 @@ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun, case Q2T_NEXUS_LOSS_SESS: TRACE(TRACE_MGMT, "qla2x00t(%ld): Doing NEXUS_LOSS_SESS", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_NEXUS_LOSS_SESS, lun, lun_size, SCST_ATOMIC, mcmd); break; case Q2T_NEXUS_LOSS: TRACE(TRACE_MGMT, "qla2x00t(%ld): Doing NEXUS_LOSS", - sess->tgt->ha->instance); + sess->tgt->vha->host_no); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_NEXUS_LOSS, lun, lun_size, SCST_ATOMIC, mcmd); break; default: PRINT_ERROR("qla2x00t(%ld): Unknown task mgmt fn 0x%x", - sess->tgt->ha->instance, fn); + sess->tgt->vha->host_no, fn); rc = -1; break; } if (rc != 0) { PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_lun() failed: %d", - sess->tgt->ha->instance, rc); + sess->tgt->vha->host_no, rc); res = -EFAULT; goto out_free; } @@ -4056,8 +4286,8 @@ out_free: goto out; } -/* pha->hardware_lock supposed to be held on entry */ -static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb) +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_handle_task_mgmt(scsi_qla_host_t *vha, void *iocb) { int res = 0; struct q2t_tgt *tgt; @@ -4066,10 +4296,11 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb) uint16_t lun_data; int lun_size; int fn; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - tgt = ha->tgt; + tgt = vha->tgt; if (IS_FWI2_CAPABLE(ha)) { atio7_entry_t *a = (atio7_entry_t *)iocb; lun = (uint8_t *)&a->fcp_cmnd.lun; @@ -4088,7 +4319,7 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb) if (sess == NULL) { TRACE_MGMT_DBG("qla2x00t(%ld): task mgmt fn 0x%x for " - "non-existent session", ha->instance, fn); + "non-existent session", vha->host_no, fn); res = q2t_sched_sess_work(tgt, Q2T_SESS_WORK_TM, iocb, IS_FWI2_CAPABLE(ha) ? sizeof(atio7_entry_t) : sizeof(notify_entry_t)); @@ -4104,8 +4335,8 @@ out: return res; } -/* pha->hardware_lock supposed to be held on entry */ -static int __q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb, +/* ha->hardware_lock supposed to be held on entry */ +static int __q2t_abort_task(scsi_qla_host_t *vha, notify_entry_t *iocb, struct q2t_sess *sess) { int res, rc; @@ -4116,7 +4347,7 @@ static int __q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb, mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); if (mcmd == NULL) { PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed", - ha->instance, __func__); + vha->host_no, __func__); res = -ENOMEM; goto out; } @@ -4130,7 +4361,7 @@ static int __q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb, le16_to_cpu(iocb->seq_id), SCST_ATOMIC, mcmd); if (rc != 0) { PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d", - ha->instance, rc); + vha->host_no, rc); res = -EFAULT; goto out_free; } @@ -4146,29 +4377,30 @@ out_free: goto out; } -/* pha->hardware_lock supposed to be held on entry */ -static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb) +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_abort_task(scsi_qla_host_t *vha, notify_entry_t *iocb) { int res; struct q2t_sess *sess; int loop_id; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); loop_id = GET_TARGET_ID(ha, iocb); - sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); + sess = q2t_find_sess_by_loop_id(vha->tgt, loop_id); if (sess == NULL) { TRACE_MGMT_DBG("qla2x00t(%ld): task abort for unexisting " - "session", ha->instance); - res = q2t_sched_sess_work(ha->tgt, Q2T_SESS_WORK_ABORT, iocb, + "session", vha->host_no); + res = q2t_sched_sess_work(vha->tgt, Q2T_SESS_WORK_ABORT, iocb, sizeof(*iocb)); if (res != 0) - ha->tgt->tm_to_unknown = 1; + vha->tgt->tm_to_unknown = 1; goto out; } - res = __q2t_abort_task(ha, iocb, sess); + res = __q2t_abort_task(vha, iocb, sess); out: TRACE_EXIT_RES(res); @@ -4178,24 +4410,24 @@ out: static void q2t_rscn_reg_work(struct work_struct *work) { struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, rscn_reg_work); - scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = tgt->vha; int ret; TRACE_ENTRY(); - if ((ha->host->active_mode & MODE_INITIATOR) == 0) { + if ((vha->host->active_mode & MODE_INITIATOR) == 0) { /* * The QLogic firmware and qla2xxx do not register for RSCNs in * target-only mode, so do that explicitly. */ - ret = qla2x00_send_change_request(ha, 0x3, ha->vp_idx); - if (ret != QLA_SUCCESS) + ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx); + if (ret != QLA_SUCCESS) { PRINT_INFO("qla2x00t(%ld): RSCN registration failed: " "%#x (OK for non-fabric setups)", - ha->host_no, ret); - else + vha->host_no, ret); + } else TRACE_MGMT_DBG("qla2x00t(%ld): RSCN registration succeeded", - ha->host_no); + vha->host_no); } TRACE_EXIT(); @@ -4203,18 +4435,16 @@ static void q2t_rscn_reg_work(struct work_struct *work) } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb) +static int q24_handle_els(scsi_qla_host_t *vha, notify24xx_entry_t *iocb) { - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; int res = 1; /* send notify ack */ - struct q2t_sess *sess; - int loop_id; TRACE_ENTRY(); - TRACE_MGMT_DBG("qla2x00t(%ld): ELS opcode %x", ha->instance, + TRACE_MGMT_DBG("qla2x00t(%ld): ELS opcode %x", vha->host_no, iocb->status_subcode); switch (iocb->status_subcode) { @@ -4226,51 +4456,23 @@ static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb) */ schedule_work(&tgt->rscn_reg_work); break; + case ELS_FLOGI: case ELS_PRLI: break; case ELS_LOGO: case ELS_PRLO: - res = q2t_reset(ha, iocb, Q2T_NEXUS_LOSS_SESS); - - /* The peer logged out - if we have a session, we'll drop it */ - if (IS_FWI2_CAPABLE(ha)) { - notify24xx_entry_t *n = (notify24xx_entry_t *)iocb; - loop_id = le16_to_cpu(n->nport_handle); - } else - loop_id = GET_TARGET_ID(ha, (notify_entry_t *)iocb); - - if (loop_id != 0xFFFF) { - sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); - if (sess) { - TRACE_MGMT_DBG("qla2x00t(%ld): port logged out - scheduling session %p for deletion " - "(port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, loop_id %d)", - ha->instance, sess, - sess->port_name[0], sess->port_name[1], - sess->port_name[2], sess->port_name[3], - sess->port_name[4], sess->port_name[5], - sess->port_name[6], sess->port_name[7], - loop_id); - - q2t_schedule_sess_for_deletion(sess); - - } else { - /* no session - no action required */ - } - } else { - PRINT_ERROR("qla2x00t(%ld): invalid loop id " - "%x for ELS opcode %x", ha->instance, - loop_id, iocb->status_subcode); - } + case ELS_TPRLO: + res = q2t_reset(vha, iocb, Q2T_NEXUS_LOSS_SESS); break; case ELS_PDISC: case ELS_ADISC: { - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; if (tgt->link_reinit_iocb_pending) { - q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0); + q24_send_notify_ack(vha, &tgt->link_reinit_iocb, 0, 0, 0); tgt->link_reinit_iocb_pending = 0; } break; @@ -4278,9 +4480,9 @@ static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb) default: PRINT_ERROR("qla2x00t(%ld): Unsupported ELS command %x " - "received", ha->instance, iocb->status_subcode); + "received", vha->host_no, iocb->status_subcode); #if 0 - res = q2t_reset(ha, iocb, Q2T_NEXUS_LOSS_SESS); + res = q2t_reset(vha, iocb, Q2T_NEXUS_LOSS_SESS); #endif break; } @@ -4332,7 +4534,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset) } if (first_sg == -1) { PRINT_ERROR("qla2x00t(%ld): Wrong offset %d, buf length %d", - cmd->tgt->ha->instance, offset, cmd->bufflen); + cmd->tgt->vha->host_no, offset, cmd->bufflen); res = -EINVAL; goto out; } @@ -4345,7 +4547,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset) sg = kmalloc(cnt * sizeof(sg[0]), GFP_KERNEL); if (sg == NULL) { PRINT_ERROR("qla2x00t(%ld): Unable to allocate cut " - "SG (len %zd)", cmd->tgt->ha->instance, + "SG (len %zd)", cmd->tgt->vha->host_no, cnt * sizeof(sg[0])); res = -ENOMEM; goto out; @@ -4421,8 +4623,8 @@ static inline int q2t_srr_adjust_data(struct q2t_cmd *cmd, *xmit_type = Q2T_XMIT_ALL; if (rel_offs < 0) { - PRINT_ERROR("qla2x00t(%ld): SRR rel_offs (%d) " - "< 0", cmd->tgt->ha->instance, rel_offs); + PRINT_ERROR("qla2x00t(%ld): SRR rel_offs (%d) < 0", + cmd->tgt->vha->host_no, rel_offs); res = -1; } else if (rel_offs == cmd->bufflen) *xmit_type = Q2T_XMIT_STATUS; @@ -4433,12 +4635,12 @@ static inline int q2t_srr_adjust_data(struct q2t_cmd *cmd, } /* No locks, thread context */ -static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, +static void q24_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio, struct srr_imm *imm) { notify24xx_entry_t *ntfy = &imm->imm.notify_entry24; struct q2t_cmd *cmd = sctio->cmd; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); @@ -4446,10 +4648,10 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, switch (ntfy->srr_ui) { case SRR_IU_STATUS: - spin_lock_irq(&pha->hardware_lock); - q24_send_notify_ack(ha, ntfy, + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(vha, ntfy, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); __q24_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: @@ -4461,15 +4663,15 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&pha->hardware_lock); - q24_send_notify_ack(ha, ntfy, + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(vha, ntfy, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); __q24_xmit_response(cmd, xmit_type); } else { PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd " "without them (tag %d, SCSI status %d, dir %d)," - " reject", ha->instance, cmd->tag, + " reject", vha->host_no, cmd->tag, scst_cmd_get_status(&cmd->scst_cmd), scst_cmd_get_data_direction(&cmd->scst_cmd)); goto out_reject; @@ -4485,16 +4687,16 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&pha->hardware_lock); - q24_send_notify_ack(ha, ntfy, + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(vha, ntfy, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); if (xmit_type & Q2T_XMIT_DATA) __q2t_rdy_to_xfer(cmd); } else { PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd " "without them (tag %d, SCSI status %d, dir %d)," - " reject", ha->instance, cmd->tag, + " reject", vha->host_no, cmd->tag, scst_cmd_get_status(&cmd->scst_cmd), scst_cmd_get_data_direction(&cmd->scst_cmd)); goto out_reject; @@ -4502,7 +4704,7 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, break; default: PRINT_ERROR("qla2x00t(%ld): Unknown srr_ui value %x", - ha->instance, ntfy->srr_ui); + vha->host_no, ntfy->srr_ui); goto out_reject; } @@ -4511,8 +4713,8 @@ out: return; out_reject: - spin_lock_irq(&pha->hardware_lock); - q24_send_notify_ack(ha, ntfy, NOTIFY_ACK_SRR_FLAGS_REJECT, + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(vha, ntfy, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); if (cmd->state == Q2T_STATE_NEED_DATA) { @@ -4522,18 +4724,18 @@ out_reject: scst_rx_data(&cmd->scst_cmd, SCST_RX_STATUS_ERROR_SENSE_SET, SCST_CONTEXT_THREAD); } else - q24_send_term_exchange(ha, cmd, &cmd->atio.atio7, 1); - spin_unlock_irq(&pha->hardware_lock); + q24_send_term_exchange(vha, cmd, &cmd->atio.atio7, 1); + spin_unlock_irq(&ha->hardware_lock); goto out; } /* No locks, thread context */ -static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, +static void q2x_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio, struct srr_imm *imm) { notify_entry_t *ntfy = &imm->imm.notify_entry; struct q2t_cmd *cmd = sctio->cmd; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); @@ -4541,10 +4743,10 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, switch (ntfy->srr_ui) { case SRR_IU_STATUS: - spin_lock_irq(&pha->hardware_lock); - q2x_send_notify_ack(ha, ntfy, 0, 0, 0, + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(vha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); __q2x_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: @@ -4555,15 +4757,15 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&pha->hardware_lock); - q2x_send_notify_ack(ha, ntfy, 0, 0, 0, + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(vha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); __q2x_xmit_response(cmd, xmit_type); } else { PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd " "without them (tag %d, SCSI status %d), " - "reject", ha->instance, cmd->tag, + "reject", vha->host_no, cmd->tag, scst_cmd_get_status(&cmd->scst_cmd)); goto out_reject; } @@ -4577,23 +4779,23 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&pha->hardware_lock); - q2x_send_notify_ack(ha, ntfy, 0, 0, 0, + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(vha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); if (xmit_type & Q2T_XMIT_DATA) __q2t_rdy_to_xfer(cmd); } else { PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd " "without them (tag %d, SCSI status %d), " - "reject", ha->instance, cmd->tag, + "reject", vha->host_no, cmd->tag, scst_cmd_get_status(&cmd->scst_cmd)); goto out_reject; } break; default: PRINT_ERROR("qla2x00t(%ld): Unknown srr_ui value %x", - ha->instance, ntfy->srr_ui); + vha->host_no, ntfy->srr_ui); goto out_reject; } @@ -4602,8 +4804,8 @@ out: return; out_reject: - spin_lock_irq(&pha->hardware_lock); - q2x_send_notify_ack(ha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(vha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); if (cmd->state == Q2T_STATE_NEED_DATA) { @@ -4613,33 +4815,33 @@ out_reject: scst_rx_data(&cmd->scst_cmd, SCST_RX_STATUS_ERROR_SENSE_SET, SCST_CONTEXT_THREAD); } else - q2x_send_term_exchange(ha, cmd, &cmd->atio.atio2x, 1); - spin_unlock_irq(&pha->hardware_lock); + q2x_send_term_exchange(vha, cmd, &cmd->atio.atio2x, 1); + spin_unlock_irq(&ha->hardware_lock); goto out; } -static void q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm, +static void q2t_reject_free_srr_imm(scsi_qla_host_t *vha, struct srr_imm *imm, int ha_locked) { - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; if (!ha_locked) - spin_lock_irq(&pha->hardware_lock); + spin_lock_irq(&ha->hardware_lock); if (IS_FWI2_CAPABLE(ha)) { - q24_send_notify_ack(ha, &imm->imm.notify_entry24, + q24_send_notify_ack(vha, &imm->imm.notify_entry24, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); } else { - q2x_send_notify_ack(ha, &imm->imm.notify_entry, + q2x_send_notify_ack(vha, &imm->imm.notify_entry, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); } if (!ha_locked) - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); kfree(imm); return; @@ -4648,8 +4850,9 @@ static void q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm, static void q2t_handle_srr_work(struct work_struct *work) { struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, srr_work); - scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = tgt->vha; struct srr_ctio *sctio; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); @@ -4671,8 +4874,8 @@ restart: PRINT_ERROR("qla2x00t(%ld): There must " "be only one IMM SRR per CTIO SRR " "(IMM SRR %p, id %d, CTIO %p", - ha->instance, i, i->srr_id, sctio); - q2t_reject_free_srr_imm(ha, i, 0); + vha->host_no, i, i->srr_id, sctio); + q2t_reject_free_srr_imm(vha, i, 0); } else imm = i; } @@ -4707,9 +4910,9 @@ restart: cmd->sg_cnt, cmd->offset); if (IS_FWI2_CAPABLE(ha)) - q24_handle_srr(ha, sctio, imm); + q24_handle_srr(vha, sctio, imm); else - q2x_handle_srr(ha, sctio, imm); + q2x_handle_srr(vha, sctio, imm); kfree(imm); kfree(sctio); @@ -4721,18 +4924,19 @@ restart: return; } -/* pha->hardware_lock supposed to be held on entry */ -static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb) +/* ha->hardware_lock supposed to be held on entry */ +static void q2t_prepare_srr_imm(scsi_qla_host_t *vha, void *iocb) { struct srr_imm *imm; - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; notify_entry_t *iocb2x = (notify_entry_t *)iocb; notify24xx_entry_t *iocb24 = (notify24xx_entry_t *)iocb; struct srr_ctio *sctio; + struct qla_hw_data *ha = vha->hw; tgt->imm_srr_id++; - TRACE(TRACE_MGMT, "qla2x00t(%ld): SRR received", ha->instance); + TRACE(TRACE_MGMT, "qla2x00t(%ld): SRR received", vha->host_no); imm = kzalloc(sizeof(*imm), GFP_ATOMIC); if (imm != NULL) { @@ -4762,7 +4966,7 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb) TRACE(TRACE_MGMT, "qla2x00t(%ld): imm_srr_id " "== ctio_srr_id (%d), but there is no " "corresponding SRR CTIO, deleting IMM " - "SRR %p", ha->instance, tgt->ctio_srr_id, + "SRR %p", vha->host_no, tgt->ctio_srr_id, imm); list_del(&imm->srr_list_entry); @@ -4777,7 +4981,7 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb) struct srr_ctio *ts; PRINT_ERROR("qla2x00t(%ld): Unable to allocate SRR IMM " - "entry, SRR request will be rejected", ha->instance); + "entry, SRR request will be rejected", vha->host_no); /* IRQ is already OFF */ spin_lock(&tgt->srr_lock); @@ -4788,10 +4992,10 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb) "(id %d)", sctio, sctio->srr_id); list_del(&sctio->srr_list_entry); if (IS_FWI2_CAPABLE(ha)) { - q24_send_term_exchange(ha, sctio->cmd, + q24_send_term_exchange(vha, sctio->cmd, &sctio->cmd->atio.atio7, 1); } else { - q2x_send_term_exchange(ha, sctio->cmd, + q2x_send_term_exchange(vha, sctio->cmd, &sctio->cmd->atio.atio2x, 1); } kfree(sctio); @@ -4806,12 +5010,12 @@ out: out_reject: if (IS_FWI2_CAPABLE(ha)) { - q24_send_notify_ack(ha, iocb24, + q24_send_notify_ack(vha, iocb24, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); } else { - q2x_send_notify_ack(ha, iocb2x, + q2x_send_notify_ack(vha, iocb2x, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); @@ -4820,15 +5024,16 @@ out_reject: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb) +static void q2t_handle_imm_notify(scsi_qla_host_t *vha, void *iocb) { uint16_t status; uint32_t add_flags = 0; int send_notify_ack = 1; notify_entry_t *iocb2x = (notify_entry_t *)iocb; notify24xx_entry_t *iocb24 = (notify24xx_entry_t *)iocb; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); @@ -4842,12 +5047,12 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb) { if (IS_FWI2_CAPABLE(ha)) { TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP reset (loop %#x), " - "subcode %x", ha->instance, + "subcode %x", vha->host_no, le16_to_cpu(iocb24->nport_handle), iocb24->status_subcode); } else { TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP reset (I %#x)", - ha->instance, GET_TARGET_ID(ha, iocb2x)); + vha->host_no, GET_TARGET_ID(ha, iocb2x)); /* set the Clear LIP reset event flag */ add_flags |= NOTIFY_ACK_CLEAR_LIP_RESET; } @@ -4861,13 +5066,13 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb) case IMM_NTFY_LIP_LINK_REINIT: { - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; TRACE(TRACE_MGMT, "qla2x00t(%ld): LINK REINIT (loop %#x, " - "subcode %x)", ha->instance, + "subcode %x)", vha->host_no, le16_to_cpu(iocb24->nport_handle), iocb24->status_subcode); if (tgt->link_reinit_iocb_pending) - q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0); + q24_send_notify_ack(vha, &tgt->link_reinit_iocb, 0, 0, 0); memcpy(&tgt->link_reinit_iocb, iocb24, sizeof(*iocb24)); tgt->link_reinit_iocb_pending = 1; /* @@ -4881,88 +5086,91 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb) case IMM_NTFY_PORT_LOGOUT: if (IS_FWI2_CAPABLE(ha)) { TRACE(TRACE_MGMT, "qla2x00t(%ld): Port logout (loop " - "%#x, subcode %x)", ha->instance, + "%#x, subcode %x)", vha->host_no, le16_to_cpu(iocb24->nport_handle), iocb24->status_subcode); } else { TRACE(TRACE_MGMT, "qla2x00t(%ld): Port logout (S " - "%08x -> L %#x)", ha->instance, + "%08x -> L %#x)", vha->host_no, le16_to_cpu(iocb2x->seq_id), le16_to_cpu(iocb2x->lun)); } - if (q2t_reset(ha, iocb, Q2T_NEXUS_LOSS_SESS) == 0) + if (q2t_reset(vha, iocb, Q2T_NEXUS_LOSS_SESS) == 0) send_notify_ack = 0; /* The sessions will be cleared in the callback, if needed */ break; case IMM_NTFY_GLBL_TPRLO: TRACE(TRACE_MGMT, "qla2x00t(%ld): Global TPRLO (%x)", - ha->instance, status); - if (q2t_reset(ha, iocb, Q2T_NEXUS_LOSS) == 0) + vha->host_no, status); + if (q2t_reset(vha, iocb, Q2T_NEXUS_LOSS) == 0) send_notify_ack = 0; /* The sessions will be cleared in the callback, if needed */ break; case IMM_NTFY_PORT_CONFIG: TRACE(TRACE_MGMT, "qla2x00t(%ld): Port config changed (%x)", - ha->instance, status); + vha->host_no, status); break; - case IMM_NTFY_GLBL_LOGO: - PRINT_WARNING("qla2x00t(%ld): Link failure detected", - ha->instance); + case IMM_NTFY_LINK_FAILURE: + if (IS_FWI2_CAPABLE(ha)) + TRACE(TRACE_MGMT, "qla2x00t(%ld): Link failure detected", + vha->host_no); + else + TRACE(TRACE_MGMT, "qla2x00t(%ld): Global LOGO received", + vha->host_no); /* I_T nexus loss */ - if (q2t_reset(ha, iocb, Q2T_NEXUS_LOSS) == 0) + if (q2t_reset(vha, iocb, Q2T_NEXUS_LOSS) == 0) send_notify_ack = 0; break; case IMM_NTFY_IOCB_OVERFLOW: PRINT_ERROR("qla2x00t(%ld): Cannot provide requested " "capability (IOCB overflowed the immediate notify " - "resource count)", ha->instance); + "resource count)", vha->host_no); break; case IMM_NTFY_ABORT_TASK: TRACE(TRACE_MGMT, "qla2x00t(%ld): Abort Task (S %08x I %#x -> " - "L %#x)", ha->instance, le16_to_cpu(iocb2x->seq_id), + "L %#x)", vha->host_no, le16_to_cpu(iocb2x->seq_id), GET_TARGET_ID(ha, iocb2x), le16_to_cpu(iocb2x->lun)); - if (q2t_abort_task(ha, iocb2x) == 0) + if (q2t_abort_task(vha, iocb2x) == 0) send_notify_ack = 0; break; case IMM_NTFY_RESOURCE: - PRINT_ERROR("qla2x00t(%ld): Out of resources, host %ld", - ha->instance, ha->host_no); + PRINT_ERROR("qla2x00t(%ld): Out of resources", vha->host_no); break; case IMM_NTFY_MSG_RX: TRACE(TRACE_MGMT, "qla2x00t(%ld): Immediate notify task %x", - ha->instance, iocb2x->task_flags); - if (q2t_handle_task_mgmt(ha, iocb2x) == 0) + vha->host_no, iocb2x->task_flags); + if (q2t_handle_task_mgmt(vha, iocb2x) == 0) send_notify_ack = 0; break; case IMM_NTFY_ELS: - if (q24_handle_els(ha, iocb24) == 0) + if (q24_handle_els(vha, iocb24) == 0) send_notify_ack = 0; break; case IMM_NTFY_SRR: - q2t_prepare_srr_imm(ha, iocb); + q2t_prepare_srr_imm(vha, iocb); send_notify_ack = 0; break; default: PRINT_ERROR("qla2x00t(%ld): Received unknown immediate " - "notify status %x", ha->instance, status); + "notify status %x", vha->host_no, status); break; } if (send_notify_ack) { if (IS_FWI2_CAPABLE(ha)) - q24_send_notify_ack(ha, iocb24, 0, 0, 0); + q24_send_notify_ack(vha, iocb24, 0, 0, 0); else - q2x_send_notify_ack(ha, iocb2x, add_flags, 0, 0, 0, + q2x_send_notify_ack(vha, iocb2x, add_flags, 0, 0, 0, 0, 0); } @@ -4971,27 +5179,28 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb) } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q2x_send_busy(scsi_qla_host_t *ha, atio_entry_t *atio) +static void q2x_send_busy(scsi_qla_host_t *vha, atio_entry_t *atio) { ctio_ret_entry_t *ctio; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); /* Sending marker isn't necessary, since we called from ISR */ - ctio = (ctio_ret_entry_t *)q2t_req_pkt(ha); + ctio = (ctio_ret_entry_t *)q2t_req_pkt(vha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } ctio->entry_type = CTIO_RET_TYPE; ctio->entry_count = 1; ctio->handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; - ctio->scsi_status = cpu_to_le16(SAM_STAT_BUSY); + ctio->scsi_status = __constant_cpu_to_le16(SAM_STAT_BUSY); ctio->residual = atio->data_length; if (ctio->residual != 0) ctio->scsi_status |= SS_RESIDUAL_UNDER; @@ -5000,9 +5209,9 @@ static void q2x_send_busy(scsi_qla_host_t *ha, atio_entry_t *atio) SET_TARGET_ID(ha, ctio->target, GET_TARGET_ID(ha, atio)); ctio->rx_id = atio->rx_id; - ctio->flags = cpu_to_le16(OF_SSTS | OF_FAST_POST | + ctio->flags = __constant_cpu_to_le16(OF_SSTS | OF_FAST_POST | OF_NO_DATA | OF_SS_MODE_1); - ctio->flags |= cpu_to_le16(OF_INC_RC); + ctio->flags |= __constant_cpu_to_le16(OF_INC_RC); /* * CTIO from fw w/o scst_cmd doesn't provide enough info to retry it, * if the explicit confirmation is used. @@ -5010,7 +5219,7 @@ static void q2x_send_busy(scsi_qla_host_t *ha, atio_entry_t *atio) TRACE_BUFFER("CTIO BUSY packet data", ctio, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out: TRACE_EXIT(); @@ -5018,9 +5227,9 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, +static void q24_send_busy(scsi_qla_host_t *vha, atio7_entry_t *atio, uint16_t status) { ctio7_status1_entry_t *ctio; @@ -5035,7 +5244,7 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, * being deleted sessions as well and use CTIO7_NHANDLE_UNRECOGNIZED, * if we can't find sess. */ - sess = q2t_find_sess_by_s_id_include_deleted(ha->tgt, + sess = q2t_find_sess_by_s_id_include_deleted(vha->tgt, atio->fcp_hdr.s_id); if (sess != NULL) loop_id = sess->loop_id; @@ -5044,10 +5253,10 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, /* Sending marker isn't necessary, since we called from ISR */ - ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(vha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " - "request packet", ha->instance, __func__); + "request packet", vha->host_no, __func__); goto out; } @@ -5055,13 +5264,13 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, ctio->common.entry_count = 1; ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; ctio->common.nport_handle = loop_id; - ctio->common.timeout = cpu_to_le16(Q2T_TIMEOUT); - ctio->common.vp_index = ha->vp_idx; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = vha->vp_idx; ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; ctio->common.exchange_addr = atio->exchange_addr; - ctio->flags = (atio->attr << 9) | cpu_to_le16( + ctio->flags = (atio->attr << 9) | __constant_cpu_to_le16( CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS | CTIO7_FLAGS_DONT_RET_CTIO); /* @@ -5077,29 +5286,29 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, TRACE_BUFFER("CTIO7 BUSY packet data", ctio, REQUEST_ENTRY_SIZE); - q2t_exec_queue(ha); + q2t_exec_queue(vha); out: TRACE_EXIT(); return; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ /* called via callback from qla2xxx */ -static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio) +static void q24_atio_pkt(scsi_qla_host_t *vha, atio7_entry_t *atio) { int rc; - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; TRACE_ENTRY(); if (unlikely(tgt == NULL)) { - TRACE_MGMT_DBG("ATIO pkt, but no tgt (ha %p)", ha); + TRACE_MGMT_DBG("ATIO pkt, but no tgt (vha %p)", vha); goto out; } TRACE(TRACE_SCSI, "qla2x00t(%ld): ATIO pkt %p: type %02x count %02x", - ha->instance, atio, atio->entry_type, atio->entry_count); + vha->host_no, atio, atio->entry_type, atio->entry_count); /* * In tgt_stop mode we also should allow all requests to pass. @@ -5112,10 +5321,10 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio) case ATIO_TYPE7: TRACE_DBG("ATIO_TYPE7 instance %ld, lun %Lx, read/write %d/%d, " "add_cdb_len %d, data_length %04x, s_id %x:%x:%x", - ha->instance, atio->fcp_cmnd.lun, atio->fcp_cmnd.rddata, + vha->host_no, atio->fcp_cmnd.lun, atio->fcp_cmnd.rddata, atio->fcp_cmnd.wrdata, atio->fcp_cmnd.add_cdb_len, - get_unaligned_be32( - &atio->fcp_cmnd.add_cdb[atio->fcp_cmnd.add_cdb_len]), + be32_to_cpu(get_unaligned((uint32_t *) + &atio->fcp_cmnd.add_cdb[atio->fcp_cmnd.add_cdb_len])), atio->fcp_hdr.s_id[0], atio->fcp_hdr.s_id[1], atio->fcp_hdr.s_id[2]); TRACE_BUFFER("Incoming ATIO7 packet data", atio, @@ -5124,28 +5333,28 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio) sizeof(atio->fcp_cmnd.cdb)); if (unlikely(atio->exchange_addr == ATIO_EXCHANGE_ADDRESS_UNKNOWN)) { - TRACE(TRACE_MINOR, "qla2x00t(%ld): ATIO_TYPE7 " + TRACE(TRACE_OUT_OF_MEM, "qla2x00t(%ld): ATIO_TYPE7 " "received with UNKNOWN exchange address, " - "sending QUEUE_FULL", ha->instance); - q24_send_busy(ha, atio, SAM_STAT_TASK_SET_FULL); + "sending QUEUE_FULL", vha->host_no); + q24_send_busy(vha, atio, SAM_STAT_TASK_SET_FULL); break; } if (likely(atio->fcp_cmnd.task_mgmt_flags == 0)) - rc = q2t_send_cmd_to_scst(ha, (atio_t *)atio); + rc = q2t_send_cmd_to_scst(vha, (atio_t *)atio); else - rc = q2t_handle_task_mgmt(ha, atio); + rc = q2t_handle_task_mgmt(vha, atio); if (unlikely(rc != 0)) { if (rc == -ESRCH) { #if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ - q24_send_busy(ha, atio, SAM_STAT_BUSY); + q24_send_busy(vha, atio, SAM_STAT_BUSY); #else - q24_send_term_exchange(ha, NULL, atio, 1); + q24_send_term_exchange(vha, NULL, atio, 1); #endif } else { PRINT_INFO("qla2x00t(%ld): Unable to send " "command to SCST, sending BUSY status", - ha->instance); - q24_send_busy(ha, atio, SAM_STAT_BUSY); + vha->host_no); + q24_send_busy(vha, atio, SAM_STAT_BUSY); } } break; @@ -5155,18 +5364,18 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio) notify_entry_t *pkt = (notify_entry_t *)atio; if (unlikely(pkt->entry_status != 0)) { PRINT_ERROR("qla2x00t(%ld): Received ATIO packet %x " - "with error status %x", ha->instance, + "with error status %x", vha->host_no, pkt->entry_type, pkt->entry_status); break; } TRACE_DBG("%s", "IMMED_NOTIFY ATIO"); - q2t_handle_imm_notify(ha, pkt); + q2t_handle_imm_notify(vha, pkt); break; } default: PRINT_ERROR("qla2x00t(%ld): Received unknown ATIO atio " - "type %x", ha->instance, atio->entry_type); + "type %x", vha->host_no, atio->entry_type); break; } @@ -5177,22 +5386,22 @@ out: return; } -/* pha->hardware_lock supposed to be held on entry */ +/* ha->hardware_lock supposed to be held on entry */ /* called via callback from qla2xxx */ -static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) +static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt) { - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; TRACE_ENTRY(); if (unlikely(tgt == NULL)) { PRINT_ERROR("qla2x00t(%ld): Response pkt %x received, but no " - "tgt (ha %p)", ha->instance, pkt->entry_type, ha); + "tgt (vha %p)", vha->host_no, pkt->entry_type, vha); goto out; } TRACE(TRACE_SCSI, "qla2x00t(%ld): pkt %p: T %02x C %02x S %02x " - "handle %#x", ha->instance, pkt, pkt->entry_type, + "handle %#x", vha->host_no, pkt, pkt->entry_type, pkt->entry_count, pkt->entry_status, pkt->handle); /* @@ -5202,7 +5411,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) if (unlikely(pkt->entry_status != 0)) { PRINT_ERROR("qla2x00t(%ld): Received response packet %x " - "with error status %x", ha->instance, pkt->entry_type, + "with error status %x", vha->host_no, pkt->entry_type, pkt->entry_status); switch (pkt->entry_type) { case ACCEPT_TGT_IO_TYPE: @@ -5221,10 +5430,10 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) { ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt; TRACE_DBG("CTIO_TYPE7: instance %ld", - ha->instance); + vha->host_no); TRACE_BUFFER("Incoming CTIO7 packet data", entry, REQUEST_ENTRY_SIZE); - q2t_do_ctio_completion(ha, entry->handle, + q2t_do_ctio_completion(vha, entry->handle, le16_to_cpu(entry->status)|(pkt->entry_status << 16), entry); break; @@ -5238,35 +5447,35 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) TRACE_DBG("ACCEPT_TGT_IO instance %ld status %04x " "lun %04x read/write %d data_length %04x " "target_id %02x rx_id %04x ", - ha->instance, le16_to_cpu(atio->status), + vha->host_no, le16_to_cpu(atio->status), le16_to_cpu(atio->lun), atio->execution_codes, le32_to_cpu(atio->data_length), - GET_TARGET_ID(ha, atio), atio->rx_id); + GET_TARGET_ID(vha->hw, atio), atio->rx_id); TRACE_BUFFER("Incoming ATIO packet data", atio, REQUEST_ENTRY_SIZE); - if (atio->status != cpu_to_le16(ATIO_CDB_VALID)) { + if (atio->status != __constant_cpu_to_le16(ATIO_CDB_VALID)) { PRINT_ERROR("qla2x00t(%ld): ATIO with error " - "status %x received", ha->instance, + "status %x received", vha->host_no, le16_to_cpu(atio->status)); break; } TRACE_BUFFER("Incoming ATIO packet data", atio, REQUEST_ENTRY_SIZE); PRINT_BUFF_FLAG(TRACE_SCSI, "FCP CDB", atio->cdb, sizeof(atio->cdb)); - rc = q2t_send_cmd_to_scst(ha, (atio_t *)atio); + rc = q2t_send_cmd_to_scst(vha, (atio_t *)atio); if (unlikely(rc != 0)) { if (rc == -ESRCH) { #if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ - q2x_send_busy(ha, atio); + q2x_send_busy(vha, atio); #else - q2x_send_term_exchange(ha, NULL, atio, 1); + q2x_send_term_exchange(vha, NULL, atio, 1); #endif } else { PRINT_INFO("qla2x00t(%ld): Unable to send " "command to SCST, sending BUSY status", - ha->instance); - q2x_send_busy(ha, atio); + vha->host_no); + q2x_send_busy(vha, atio); } } } @@ -5275,10 +5484,10 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) case CONTINUE_TGT_IO_TYPE: { ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt; - TRACE_DBG("CONTINUE_TGT_IO: instance %ld", ha->instance); + TRACE_DBG("CONTINUE_TGT_IO: instance %ld", vha->host_no); TRACE_BUFFER("Incoming CTIO packet data", entry, REQUEST_ENTRY_SIZE); - q2t_do_ctio_completion(ha, entry->handle, + q2t_do_ctio_completion(vha, entry->handle, le16_to_cpu(entry->status)|(pkt->entry_status << 16), entry); break; @@ -5287,10 +5496,10 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) case CTIO_A64_TYPE: { ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt; - TRACE_DBG("CTIO_A64: instance %ld", ha->instance); + TRACE_DBG("CTIO_A64: instance %ld", vha->host_no); TRACE_BUFFER("Incoming CTIO_A64 packet data", entry, REQUEST_ENTRY_SIZE); - q2t_do_ctio_completion(ha, entry->handle, + q2t_do_ctio_completion(vha, entry->handle, le16_to_cpu(entry->status)|(pkt->entry_status << 16), entry); break; @@ -5298,7 +5507,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) case IMMED_NOTIFY_TYPE: TRACE_DBG("%s", "IMMED_NOTIFY"); - q2t_handle_imm_notify(ha, (notify_entry_t *)pkt); + q2t_handle_imm_notify(vha, (notify_entry_t *)pkt); break; case NOTIFY_ACK_TYPE: @@ -5310,22 +5519,22 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) TRACE_BUFFER("Incoming NOTIFY_ACK packet data", pkt, RESPONSE_ENTRY_SIZE); tgt->notify_ack_expected--; - if (entry->status != cpu_to_le16(NOTIFY_ACK_SUCCESS)) { + if (entry->status != __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) { PRINT_ERROR("qla2x00t(%ld): NOTIFY_ACK " - "failed %x", ha->instance, + "failed %x", vha->host_no, le16_to_cpu(entry->status)); } } else { PRINT_ERROR("qla2x00t(%ld): Unexpected NOTIFY_ACK " - "received", ha->instance); + "received", vha->host_no); } break; case ABTS_RECV_24XX: - TRACE_DBG("ABTS_RECV_24XX: instance %ld", ha->instance); + TRACE_DBG("ABTS_RECV_24XX: instance %ld", vha->host_no); TRACE_BUFF_FLAG(TRACE_BUFF, "Incoming ABTS_RECV " "packet data", pkt, REQUEST_ENTRY_SIZE); - q24_handle_abts(ha, (abts24_recv_entry_t *)pkt); + q24_handle_abts(vha, (abts24_recv_entry_t *)pkt); break; case ABTS_RESP_24XX: @@ -5352,16 +5561,16 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) * have to (re)terminate the exchange and * retry the abort response. */ - q24_retry_term_exchange(ha, entry); + q24_retry_term_exchange(vha, entry); } else PRINT_ERROR("qla2x00t(%ld): ABTS_RESP_24XX " - "failed %x (subcode %x:%x)", ha->instance, + "failed %x (subcode %x:%x)", vha->host_no, entry->compl_status, entry->error_subcode1, entry->error_subcode2); } } else { PRINT_ERROR("qla2x00t(%ld): Unexpected ABTS_RESP_24XX " - "received", ha->instance); + "received", vha->host_no); } break; @@ -5381,12 +5590,12 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) tgt->modify_lun_expected--; if (entry->status != MODIFY_LUN_SUCCESS) { PRINT_ERROR("qla2x00t(%ld): MODIFY_LUN " - "failed %x", ha->instance, + "failed %x", vha->host_no, entry->status); } } else { PRINT_ERROR("qla2x00t(%ld): Unexpected MODIFY_LUN " - "received", (ha != NULL) ? (long)ha->instance : -1); + "received", (vha != NULL) ? (long)vha->host_no : -1); } break; @@ -5406,15 +5615,15 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) entry->status = ENABLE_LUN_SUCCESS; } else if (entry->status != ENABLE_LUN_SUCCESS) { PRINT_ERROR("qla2x00t(%ld): ENABLE_LUN " - "failed %x", ha->instance, entry->status); - qla_clear_tgt_mode(ha); + "failed %x", vha->host_no, entry->status); + qla_clear_tgt_mode(vha); } /* else success */ break; } default: PRINT_ERROR("qla2x00t(%ld): Received unknown response pkt " - "type %x", ha->instance, pkt->entry_type); + "type %x", vha->host_no, pkt->entry_type); break; } @@ -5426,17 +5635,17 @@ out: } /* - * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire + * ha->hardware_lock supposed to be held on entry. Might drop it, then reacquire */ -static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha, +static void q2t_async_event(uint16_t code, scsi_qla_host_t *vha, uint16_t *mailbox) { - struct q2t_tgt *tgt = ha->tgt; + struct q2t_tgt *tgt = vha->tgt; TRACE_ENTRY(); if (unlikely(tgt == NULL)) { - TRACE_DBG("ASYNC EVENT %#x, but no tgt (ha %p)", code, ha); + TRACE_DBG("ASYNC EVENT %#x, but no tgt (vha %p)", code, vha); goto out; } @@ -5454,43 +5663,43 @@ static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha, case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ case MBA_ATIO_TRANSFER_ERR: /* ATIO Queue Transfer Error */ TRACE(TRACE_MGMT, "qla2x00t(%ld): System error async event %#x " - "occurred", ha->instance, code); + "occurred", vha->host_no, code); break; case MBA_LOOP_UP: TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop up occurred", - ha->instance); + vha->host_no); if (tgt->link_reinit_iocb_pending) { - q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0); + q24_send_notify_ack(vha, &tgt->link_reinit_iocb, 0, 0, 0); tgt->link_reinit_iocb_pending = 0; } break; case MBA_LIP_OCCURRED: - TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP occurred", ha->instance); + TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP occurred", vha->host_no); break; case MBA_LOOP_DOWN: TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop down occurred", - ha->instance); + vha->host_no); break; case MBA_LIP_RESET: TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP reset occurred", - ha->instance); + vha->host_no); break; case MBA_PORT_UPDATE: case MBA_RSCN_UPDATE: TRACE_MGMT_DBG("qla2x00t(%ld): Port update async event %#x " - "occurred", ha->instance, code); - /* .mark_all_devices_lost() is handled by the initiator driver */ + "occurred", vha->host_no, code); + q2t_clear_tgt_db(tgt, false); break; default: TRACE(TRACE_MGMT, "qla2x00t(%ld): Async event %#x occurred: " "ignoring (m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", - ha->instance, code, + vha->host_no, code, le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); break; @@ -5528,22 +5737,24 @@ out: } /* Must be called under tgt_mutex */ -static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, +static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *vha, const uint8_t *s_id, uint16_t loop_id) { struct q2t_sess *sess = NULL; fc_port_t *fcport = NULL; int rc, global_resets; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); retry: - global_resets = atomic_read(&ha->tgt->tgt_global_resets_count); + global_resets = atomic_read(&vha->tgt->tgt_global_resets_count); if (IS_FWI2_CAPABLE(ha)) { sBUG_ON(s_id == NULL); - rc = q24_get_loop_id(ha, s_id, &loop_id); + /* Just after LIP it can take a while, e.g. 30 seconds */ + rc = q24_get_loop_id(vha, s_id, &loop_id); if (rc != 0) { if ((s_id[0] == 0xFF) && (s_id[1] == 0xFC)) { @@ -5557,7 +5768,7 @@ retry: } else PRINT_ERROR("qla2x00t(%ld): Unable to find " "initiator with S_ID %x:%x:%x", - ha->instance, s_id[0], s_id[1], + vha->host_no, s_id[0], s_id[1], s_id[2]); goto out; } @@ -5566,7 +5777,7 @@ retry: fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); if (fcport == NULL) { PRINT_ERROR("qla2x00t(%ld): Allocation of tmp FC port failed", - ha->instance); + vha->host_no); goto out; } @@ -5574,25 +5785,25 @@ retry: fcport->loop_id = loop_id; - rc = qla2x00_get_port_database(ha, fcport, 0); + rc = qla2x00_get_port_database(vha, fcport, 0); if (rc != QLA_SUCCESS) { TRACE_MGMT_DBG("qla2x00t(%ld): Failed to retrieve fcport " "information -- get_port_database() returned %x " - "(loop_id=0x%04x)", ha->instance, rc, loop_id); + "(loop_id=0x%04x)", vha->host_no, rc, loop_id); goto out_free_fcport; } - if (global_resets != atomic_read(&ha->tgt->tgt_global_resets_count)) { + if (global_resets != atomic_read(&vha->tgt->tgt_global_resets_count)) { TRACE_MGMT_DBG("qla2x00t(%ld): global reset during session " "discovery (counter was %d, new %d), retrying", - ha->instance, global_resets, - atomic_read(&ha->tgt->tgt_global_resets_count)); + vha->host_no, global_resets, + atomic_read(&vha->tgt->tgt_global_resets_count)); kfree(fcport); fcport = NULL; goto retry; } - sess = q2t_create_sess(ha, fcport, true); + sess = q2t_create_sess(vha, fcport, true); out_free_fcport: kfree(fcport); @@ -5605,8 +5816,8 @@ out: static void q2t_exec_sess_work(struct q2t_tgt *tgt, struct q2t_sess_work_param *prm) { - scsi_qla_host_t *ha = tgt->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; int rc; struct q2t_sess *sess = NULL; uint8_t *s_id = NULL; /* to hide compiler warnings */ @@ -5617,8 +5828,8 @@ static void q2t_exec_sess_work(struct q2t_tgt *tgt, TRACE_MGMT_DBG("prm %p", prm); - mutex_lock(&ha->tgt_mutex); - spin_lock_irq(&pha->hardware_lock); + mutex_lock(&vha->tgt_mutex); + spin_lock_irq(&ha->hardware_lock); if (tgt->tgt_stop) goto send; @@ -5674,9 +5885,9 @@ after_find: * We are under tgt_mutex, so a new sess can't be added * behind us. */ - spin_unlock_irq(&pha->hardware_lock); - sess = q2t_make_local_sess(ha, s_id, loop_id); - spin_lock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); + sess = q2t_make_local_sess(vha, s_id, loop_id); + spin_lock_irq(&ha->hardware_lock); /* sess has got an extra creation ref */ } @@ -5697,14 +5908,14 @@ send: goto out_term; } TRACE_MGMT_DBG("Sending work cmd %p to SCST", cmd); - rc = q2t_do_send_cmd_to_scst(ha, cmd, sess); + rc = q2t_do_send_cmd_to_scst(vha, cmd, sess); break; } case Q2T_SESS_WORK_ABORT: if (IS_FWI2_CAPABLE(ha)) - rc = __q24_handle_abts(ha, &prm->abts, sess); + rc = __q24_handle_abts(vha, &prm->abts, sess); else - rc = __q2t_abort_task(ha, &prm->tm_iocb, sess); + rc = __q2t_abort_task(vha, &prm->tm_iocb, sess); break; case Q2T_SESS_WORK_TM: { @@ -5743,8 +5954,8 @@ out_put: if (sess != NULL) q2t_sess_put(sess); - spin_unlock_irq(&pha->hardware_lock); - mutex_unlock(&ha->tgt_mutex); + spin_unlock_irq(&ha->hardware_lock); + mutex_unlock(&vha->tgt_mutex); TRACE_EXIT(); return; @@ -5760,25 +5971,25 @@ out_term: * argument */ if (IS_FWI2_CAPABLE(ha)) - q24_send_term_exchange(ha, NULL, &cmd->atio.atio7, 1); + q24_send_term_exchange(vha, NULL, &cmd->atio.atio7, 1); else - q2x_send_term_exchange(ha, NULL, &cmd->atio.atio2x, 1); + q2x_send_term_exchange(vha, NULL, &cmd->atio.atio2x, 1); q2t_free_cmd(cmd); break; } case Q2T_SESS_WORK_ABORT: if (IS_FWI2_CAPABLE(ha)) - q24_send_abts_resp(ha, &prm->abts, + q24_send_abts_resp(vha, &prm->abts, SCST_MGMT_STATUS_REJECTED, false); else - q2x_send_notify_ack(ha, &prm->tm_iocb, 0, + q2x_send_notify_ack(vha, &prm->tm_iocb, 0, 0, 0, 0, 0, 0); break; case Q2T_SESS_WORK_TM: if (IS_FWI2_CAPABLE(ha)) - q24_send_term_exchange(ha, NULL, &prm->tm_iocb2, 1); + q24_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1); else - q2x_send_notify_ack(ha, &prm->tm_iocb, 0, + q2x_send_notify_ack(vha, &prm->tm_iocb, 0, 0, 0, 0, 0, 0); break; default: @@ -5791,7 +6002,8 @@ out_term: static void q2t_sess_work_fn(struct work_struct *work) { struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, sess_work); - scsi_qla_host_t *pha = to_qla_parent(tgt->ha); + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); @@ -5819,28 +6031,28 @@ static void q2t_sess_work_fn(struct work_struct *work) } spin_unlock_irq(&tgt->sess_work_lock); - spin_lock_irq(&pha->hardware_lock); + spin_lock_irq(&ha->hardware_lock); spin_lock(&tgt->sess_work_lock); if (list_empty(&tgt->sess_works_list)) { tgt->sess_works_pending = 0; tgt->tm_to_unknown = 0; } spin_unlock(&tgt->sess_work_lock); - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); TRACE_EXIT(); return; } -/* pha->hardware_lock supposed to be held and IRQs off */ -static void q2t_cleanup_hw_pending_cmd(scsi_qla_host_t *ha, struct q2t_cmd *cmd) +/* ha->hardware_lock supposed to be held and IRQs off */ +static void q2t_cleanup_hw_pending_cmd(scsi_qla_host_t *vha, struct q2t_cmd *cmd) { uint32_t h; for (h = 0; h < MAX_OUTSTANDING_COMMANDS; h++) { - if (ha->cmds[h] == cmd) { + if (vha->cmds[h] == cmd) { TRACE_DBG("Clearing handle %d for cmd %p", h, cmd); - ha->cmds[h] = NULL; + vha->cmds[h] = NULL; break; } } @@ -5851,8 +6063,8 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) { struct q2t_cmd *cmd = container_of(scst_cmd, struct q2t_cmd, scst_cmd); struct q2t_tgt *tgt = cmd->tgt; - scsi_qla_host_t *ha = tgt->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; unsigned long flags; TRACE_ENTRY(); @@ -5860,17 +6072,17 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) TRACE_MGMT_DBG("Cmd %p HW pending for too long (state %x)", cmd, cmd->state); - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); if (cmd->sg_mapped) - q2t_unmap_sg(ha, cmd); + q2t_unmap_sg(vha, cmd); if (cmd->state == Q2T_STATE_PROCESSED) { TRACE_MGMT_DBG("Force finishing cmd %p", cmd); } else if (cmd->state == Q2T_STATE_NEED_DATA) { TRACE_MGMT_DBG("Force rx_data cmd %p", cmd); - q2t_cleanup_hw_pending_cmd(ha, cmd); + q2t_cleanup_hw_pending_cmd(vha, cmd); /* It might be sporadic, hence retriable */ scst_set_cmd_error(scst_cmd, @@ -5882,23 +6094,23 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) cmd, cmd->tag); } else { PRINT_ERROR("qla2x00t(%ld): A command in state (%d) should " - "not be HW pending", ha->instance, cmd->state); + "not be HW pending", vha->host_no, cmd->state); goto out_unlock; } - q2t_cleanup_hw_pending_cmd(ha, cmd); + q2t_cleanup_hw_pending_cmd(vha, cmd); scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_FAILED); scst_tgt_cmd_done(scst_cmd, SCST_CONTEXT_THREAD); out_unlock: - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); TRACE_EXIT(); return; } /* Must be called under tgt_host_action_mutex */ -static int q2t_add_target(scsi_qla_host_t *ha) +static int q2t_add_target(scsi_qla_host_t *vha) { int res; #ifndef CONFIG_SCST_PROC @@ -5907,12 +6119,13 @@ static int q2t_add_target(scsi_qla_host_t *ha) char *wwn; int sg_tablesize; struct q2t_tgt *tgt; + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - TRACE_DBG("Registering target for host %ld(%p)", ha->host_no, ha); + TRACE_DBG("Registering target for host %ld(%p)", vha->host_no, vha); - sBUG_ON((ha->q2t_tgt != NULL) || (ha->tgt != NULL)); + sBUG_ON((vha->q2t_tgt != NULL) || (vha->tgt != NULL)); tgt = kmem_cache_zalloc(q2t_tgt_cachep, GFP_KERNEL); if (tgt == NULL) { @@ -5921,7 +6134,7 @@ static int q2t_add_target(scsi_qla_host_t *ha) goto out; } - tgt->ha = ha; + tgt->vha = vha; init_waitqueue_head(&tgt->waitQ); INIT_WORK(&tgt->rscn_reg_work, q2t_rscn_reg_work); INIT_LIST_HEAD(&tgt->sess_list); @@ -5936,9 +6149,11 @@ static int q2t_add_target(scsi_qla_host_t *ha) INIT_WORK(&tgt->srr_work, q2t_handle_srr_work); atomic_set(&tgt->tgt_global_resets_count, 0); - ha->q2t_tgt = tgt; + vha->q2t_tgt = tgt; - res = q2t_get_target_name(ha->port_name, &wwn); + res = q2t_get_target_name(((vha->vp_idx == 0) && vha->port_name_set) ? + vha->hw->orig_hw_port_name : + vha->port_name, &wwn); if (res != 0) goto out_free; @@ -5948,36 +6163,27 @@ static int q2t_add_target(scsi_qla_host_t *ha) if (!tgt->scst_tgt) { PRINT_ERROR("qla2x00t(%ld): scst_register_target() " - "failed for host %ld(%p)", ha->instance, - ha->host_no, ha); + "failed for host %ld(%p)", vha->host_no, + vha->host_no, vha); res = -ENOMEM; goto out_free; } if (IS_FWI2_CAPABLE(ha)) { - PRINT_INFO("qla2x00t(%ld): using 64 Bit PCI " - "addressing", ha->instance); - tgt->tgt_enable_64bit_addr = 1; - /* 3 is reserved */ - sg_tablesize = - QLA_MAX_SG_24XX(ha->request_q_length - 3); - tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND_24XX; - tgt->datasegs_per_cont = DATASEGS_PER_CONT_24XX; + /* 3 is reserved */ + sg_tablesize = QLA_MAX_SG_24XX(ha->req_q_map[0]->length - 3); + tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND_24XX; + tgt->datasegs_per_cont = DATASEGS_PER_CONT_24XX; } else { - if (ha->flags.enable_64bit_addressing) { - PRINT_INFO("qla2x00t(%ld): 64 Bit PCI " - "addressing enabled", ha->instance); - tgt->tgt_enable_64bit_addr = 1; + if (ha->enable_64bit_addressing) { /* 3 is reserved */ - sg_tablesize = - QLA_MAX_SG64(ha->request_q_length - 3); + sg_tablesize = QLA_MAX_SG64(ha->req_q_map[0]->length - 3); tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND64; tgt->datasegs_per_cont = DATASEGS_PER_CONT64; } else { PRINT_INFO("qla2x00t(%ld): Using 32 Bit " - "PCI addressing", ha->instance); - sg_tablesize = - QLA_MAX_SG32(ha->request_q_length - 3); + "PCI addressing", vha->host_no); + sg_tablesize = QLA_MAX_SG32(ha->req_q_map[0]->length - 3); tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND32; tgt->datasegs_per_cont = DATASEGS_PER_CONT32; } @@ -5985,12 +6191,12 @@ static int q2t_add_target(scsi_qla_host_t *ha) #ifndef CONFIG_SCST_PROC rc = sysfs_create_link(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), - &ha->host->shost_dev.kobj, "host"); + &vha->host->shost_dev.kobj, "host"); if (rc != 0) PRINT_ERROR("qla2x00t(%ld): Unable to create \"host\" link for " - "target %s", ha->instance, + "target %s", vha->host_no, scst_get_tgt_name(tgt->scst_tgt)); - if (!ha->parent) { + if (vha->vp_idx == 0) { int i = 0; while (1) { const struct attribute *a = q2t_hw_tgt_attrs[i]; @@ -6000,7 +6206,7 @@ static int q2t_add_target(scsi_qla_host_t *ha) if (rc != 0) PRINT_ERROR("qla2x00t(%ld): Unable to create " "\"%s\" file for target %s", - ha->instance, a->name, + vha->host_no, a->name, scst_get_tgt_name(tgt->scst_tgt)); i++; } @@ -6014,7 +6220,7 @@ static int q2t_add_target(scsi_qla_host_t *ha) if (rc != 0) PRINT_ERROR("qla2x00t(%ld): Unable to create " "\"%s\" file for target %s", - ha->instance, a->name, + vha->host_no, a->name, scst_get_tgt_name(tgt->scst_tgt)); i++; } @@ -6029,18 +6235,18 @@ out: return res; out_free: - ha->q2t_tgt = NULL; + vha->q2t_tgt = NULL; kmem_cache_free(q2t_tgt_cachep, tgt); goto out; } /* Must be called under tgt_host_action_mutex */ -static int q2t_remove_target(scsi_qla_host_t *ha) +static int q2t_remove_target(scsi_qla_host_t *vha) { TRACE_ENTRY(); - TRACE_DBG("Unregistering target for host %ld(%p)", ha->host_no, ha); - scst_unregister_target(ha->q2t_tgt->scst_tgt); + TRACE_DBG("Unregistering target for host %ld(%p)", vha->host_no, vha); + scst_unregister_target(vha->q2t_tgt->scst_tgt); /* * Free of tgt happens via callback q2t_target_release * called from scst_unregister_target, so we shouldn't touch @@ -6051,84 +6257,85 @@ static int q2t_remove_target(scsi_qla_host_t *ha) return 0; } -static int q2t_host_action(scsi_qla_host_t *ha, +static int q2t_host_action(scsi_qla_host_t *vha, qla2x_tgt_host_action_t action) { int res = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; TRACE_ENTRY(); - sBUG_ON(ha == NULL); + sBUG_ON(vha == NULL); /* To sync with q2t_exit() */ if (down_read_trylock(&q2t_unreg_rwsem) == 0) goto out; - mutex_lock(&ha->tgt_host_action_mutex); + mutex_lock(&vha->tgt_host_action_mutex); switch (action) { case ADD_TARGET: - res = q2t_add_target(ha); + res = q2t_add_target(vha); break; case REMOVE_TARGET: - res = q2t_remove_target(ha); + res = q2t_remove_target(vha); break; case ENABLE_TARGET_MODE: { fc_port_t *fcport; - if (qla_tgt_mode_enabled(ha)) { + if (qla_tgt_mode_enabled(vha)) { PRINT_INFO("qla2x00t(%ld): Target mode already " - "enabled", ha->instance); + "enabled", vha->host_no); break; } - if ((ha->q2t_tgt == NULL) || (ha->tgt != NULL)) { + if ((vha->q2t_tgt == NULL) || + (vha->tgt != NULL)) { PRINT_ERROR("qla2x00t(%ld): Can't enable target mode " - "for not existing target", ha->instance); + "for not existing target", vha->host_no); break; } PRINT_INFO("qla2x00t(%ld): Enabling target mode", - ha->instance); + vha->host_no); - spin_lock_irq(&pha->hardware_lock); - ha->tgt = ha->q2t_tgt; - ha->tgt->tgt_stop = 0; - spin_unlock_irq(&pha->hardware_lock); - list_for_each_entry_rcu(fcport, &ha->fcports, list) { - q2t_fc_port_added(ha, fcport); + spin_lock_irq(&ha->hardware_lock); + vha->tgt = vha->q2t_tgt; + vha->tgt->tgt_stop = 0; + spin_unlock_irq(&ha->hardware_lock); + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { + q2t_fc_port_added(vha, fcport); } - TRACE_DBG("Enable tgt mode for host %ld(%ld,%p)", - ha->host_no, ha->instance, ha); - qla2x00_enable_tgt_mode(ha); + TRACE_DBG("Enable tgt mode for host %ld(%p)", + vha->host_no, vha); + qla2x00_enable_tgt_mode(vha); break; } case DISABLE_TARGET_MODE: - if (!qla_tgt_mode_enabled(ha)) { + if (!qla_tgt_mode_enabled(vha)) { PRINT_INFO("qla2x00t(%ld): Target mode already " - "disabled", ha->instance); + "disabled", vha->host_no); break; } PRINT_INFO("qla2x00t(%ld): Disabling target mode", - ha->instance); + vha->host_no); - sBUG_ON(ha->tgt == NULL); + sBUG_ON(vha->tgt == NULL); - q2t_target_stop(ha->tgt->scst_tgt); + q2t_target_stop(vha->tgt->scst_tgt); break; default: PRINT_ERROR("qla2x00t(%ld): %s: unsupported action %d", - ha->instance, __func__, action); + vha->host_no, __func__, action); res = -EINVAL; break; } - mutex_unlock(&ha->tgt_host_action_mutex); + mutex_unlock(&vha->tgt_host_action_mutex); up_read(&q2t_unreg_rwsem); out: @@ -6138,19 +6345,19 @@ out: static int q2t_enable_tgt(struct scst_tgt *scst_tgt, bool enable) { - struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); - scsi_qla_host_t *ha; + struct q2t_tgt *tgt = scst_tgt_get_tgt_priv(scst_tgt); + scsi_qla_host_t *vha; int res = -E_TGT_PRIV_NOT_YET_SET; if (tgt == NULL) goto out; - ha = tgt->ha; + vha = tgt->vha; if (enable) - res = q2t_host_action(ha, ENABLE_TARGET_MODE); + res = q2t_host_action(vha, ENABLE_TARGET_MODE); else - res = q2t_host_action(ha, DISABLE_TARGET_MODE); + res = q2t_host_action(vha, DISABLE_TARGET_MODE); out: return res; @@ -6158,12 +6365,12 @@ out: static bool q2t_is_tgt_enabled(struct scst_tgt *scst_tgt) { - struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + struct q2t_tgt *tgt = scst_tgt_get_tgt_priv(scst_tgt); if (tgt == NULL) return false; - return qla_tgt_mode_enabled(tgt->ha); + return qla_tgt_mode_enabled(tgt->vha); } #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ @@ -6204,14 +6411,16 @@ static int q2t_parse_wwn(const char *ns, u64 *nm) } #endif +#if ENABLE_NPIV #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED)) static ssize_t q2t_add_vtarget(const char *target_name, char *params) { int res; char *param, *p, *pp; - u64 port_name, node_name, *pnode_name = NULL; - u64 parent_host, *pparent_host = NULL; + u64 port_name, node_name; + u64 parent_host; + bool parent_host_set = false, node_name_set = false; TRACE_ENTRY(); @@ -6258,7 +6467,7 @@ static ssize_t q2t_add_vtarget(const char *target_name, char *params) res = -EINVAL; goto out; } - pnode_name = &node_name; + node_name_set = true; continue; } @@ -6269,7 +6478,7 @@ static ssize_t q2t_add_vtarget(const char *target_name, char *params) " (target %s)", pp, target_name); goto out; } - pparent_host = &parent_host; + parent_host_set = true; continue; } @@ -6279,21 +6488,17 @@ static ssize_t q2t_add_vtarget(const char *target_name, char *params) goto out; } - if (!pnode_name) { - PRINT_ERROR("qla2x00t: Missing parameter node_name (target %s)", - target_name); - res = -EINVAL; - goto out; - } + if (!node_name_set) + node_name = port_name; - if (!pparent_host) { + if (!parent_host_set) { PRINT_ERROR("qla2x00t: Missing parameter parent_host " "(target %s)", target_name); res = -EINVAL; goto out; } - res = qla2xxx_add_vtarget(&port_name, pnode_name, pparent_host); + res = qla2xxx_add_vtarget(port_name, node_name, parent_host); out: TRACE_EXIT_RES(res); @@ -6314,13 +6519,15 @@ static ssize_t q2t_del_vtarget(const char *target_name) goto out; } - res = qla2xxx_del_vtarget(&port_name); + res = qla2xxx_del_vtarget(port_name); + out: TRACE_EXIT_RES(res); return res; } #endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED))*/ +#endif /* ENABLE_NPIV */ static int q2t_get_initiator_port_transport_id(struct scst_tgt *tgt, struct scst_session *scst_sess, uint8_t **transport_id) @@ -6370,17 +6577,18 @@ static ssize_t q2t_show_expl_conf_enabled(struct kobject *kobj, { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; - scsi_qla_host_t *ha; - int res = -E_TGT_PRIV_NOT_YET_SET; + scsi_qla_host_t *vha; + ssize_t res = -E_TGT_PRIV_NOT_YET_SET; scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); tgt = scst_tgt_get_tgt_priv(scst_tgt); if (!tgt) goto out; - ha = tgt->ha; + vha = tgt->vha; - res = scnprintf(buffer, PAGE_SIZE, "%d\n%s", ha->enable_explicit_conf, - ha->enable_explicit_conf ? SCST_SYSFS_KEY_MARK "\n" : ""); + res = scnprintf(buffer, PAGE_SIZE, "%d\n%s", + vha->hw->enable_explicit_conf, + vha->hw->enable_explicit_conf ? SCST_SYSFS_KEY_MARK "\n" : ""); out: return res; @@ -6391,37 +6599,48 @@ static ssize_t q2t_store_expl_conf_enabled(struct kobject *kobj, { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; - scsi_qla_host_t *ha, *pha; - int res = -E_TGT_PRIV_NOT_YET_SET; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + int res = -E_TGT_PRIV_NOT_YET_SET, old; unsigned long flags; scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); tgt = scst_tgt_get_tgt_priv(scst_tgt); if (!tgt) goto out; - ha = tgt->ha; - pha = to_qla_parent(ha); + vha = tgt->vha; + ha = vha->hw; - spin_lock_irqsave(&pha->hardware_lock, flags); + /* Only hw port allowed here */ + sBUG_ON(vha->vp_idx != 0); + + old = ha->enable_explicit_conf; + + spin_lock_irqsave(&ha->hardware_lock, flags); switch (buffer[0]) { case '0': ha->enable_explicit_conf = 0; PRINT_INFO("qla2x00t(%ld): explicit confirmations disabled", - ha->instance); + vha->host_no); break; case '1': ha->enable_explicit_conf = 1; PRINT_INFO("qla2x00t(%ld): explicit confirmations enabled", - ha->instance); + vha->host_no); break; default: PRINT_ERROR("%s: qla2x00t(%ld): Requested action not " - "understood: %s", __func__, ha->instance, buffer); + "understood: %s", __func__, vha->host_no, buffer); break; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (ha->enable_explicit_conf != old) { + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2x00_wait_for_hba_online(vha); + } res = size; @@ -6434,19 +6653,22 @@ static ssize_t q2t_abort_isp_store(struct kobject *kobj, { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; int res = -E_TGT_PRIV_NOT_YET_SET; scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); tgt = scst_tgt_get_tgt_priv(scst_tgt); if (!tgt) goto out; - ha = tgt->ha; + vha = tgt->vha; - PRINT_INFO("qla2x00t(%ld): Aborting ISP", ha->instance); + /* Only hw port allowed here */ + sBUG_ON(vha->vp_idx != 0); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2x00_wait_for_hba_online(ha); + PRINT_INFO("qla2x00t(%ld): Aborting ISP", vha->host_no); + + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2x00_wait_for_hba_online(vha); res = size; @@ -6490,7 +6712,7 @@ static ssize_t q2t_node_name_show(struct kobject *kobj, { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; ssize_t res = -E_TGT_PRIV_NOT_YET_SET; char *wwn; uint8_t *node_name; @@ -6499,22 +6721,18 @@ static ssize_t q2t_node_name_show(struct kobject *kobj, tgt = scst_tgt_get_tgt_priv(scst_tgt); if (!tgt) goto out; - ha = tgt->ha; + vha = tgt->vha; - if (ha->parent == NULL) { - if (qla_tgt_mode_enabled(ha) || !ha->node_name_set) - node_name = ha->node_name; - else - node_name = ha->tgt_node_name; - } else - node_name = ha->node_name; + node_name = vha->node_name; res = q2t_get_target_name(node_name, &wwn); if (res != 0) goto out; res = sprintf(buf, "%s\n", wwn); - if ((ha->parent != NULL) || ha->node_name_set) + + /* For virtual ports it's always key */ + if (vha->node_name_set || (vha->vp_idx != 0)) res += sprintf(&buf[res], "%s\n", SCST_SYSFS_KEY_MARK); kfree(wwn); @@ -6528,7 +6746,7 @@ static ssize_t q2t_node_name_store(struct kobject *kobj, { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; u64 node_name, old_node_name; int res = -E_TGT_PRIV_NOT_YET_SET; @@ -6538,9 +6756,10 @@ static ssize_t q2t_node_name_store(struct kobject *kobj, tgt = scst_tgt_get_tgt_priv(scst_tgt); if (!tgt) goto out; - ha = tgt->ha; + vha = tgt->vha; - sBUG_ON(ha->parent != NULL); + /* Only hw port allowed to dynamically set node name */ + sBUG_ON(vha->vp_idx != 0); if (size == 0) goto out_default; @@ -6549,21 +6768,29 @@ static ssize_t q2t_node_name_store(struct kobject *kobj, if (res != 0) { if ((buffer[0] == '\0') || (buffer[0] == '\n')) goto out_default; - PRINT_ERROR("qla2x00t(%ld): Wrong node name", ha->instance); + PRINT_ERROR("qla2x00t(%ld): Wrong node name", vha->host_no); goto out; } - old_node_name = wwn_to_u64(ha->node_name); + old_node_name = wwn_to_u64(vha->node_name); if (old_node_name == node_name) goto out_success; - u64_to_wwn(node_name, ha->tgt_node_name); - ha->node_name_set = 1; + if (vha->node_name_set) + vha->node_name_set = (node_name != wwn_to_u64(vha->hw->orig_hw_node_name)); + else { + BUILD_BUG_ON(sizeof(vha->node_name) != sizeof(vha->hw->orig_hw_node_name)); + memcpy(vha->hw->orig_hw_node_name, vha->node_name, + sizeof(vha->node_name)); + vha->node_name_set = 1; + } + + u64_to_wwn(node_name, vha->node_name); abort: - if (qla_tgt_mode_enabled(ha)) { - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2x00_wait_for_hba_online(ha); + if (qla_tgt_mode_enabled(vha)) { + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2x00_wait_for_hba_online(vha); } out_success: @@ -6574,7 +6801,112 @@ out: return res; out_default: - ha->node_name_set = 0; + if (vha->node_name_set) + memcpy(vha->node_name, vha->hw->orig_hw_node_name, + sizeof(vha->node_name)); + vha->node_name_set = 0; + goto abort; +} + +static ssize_t q2t_port_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_tgt *scst_tgt; + struct q2t_tgt *tgt; + scsi_qla_host_t *vha; + ssize_t res = -E_TGT_PRIV_NOT_YET_SET; + char *wwn; + uint8_t *port_name; + + /* Can be called for both HW and V ports */ + + scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + if (!tgt) + goto out; + vha = tgt->vha; + + port_name = vha->port_name; + + res = q2t_get_target_name(port_name, &wwn); + if (res != 0) + goto out; + + res = sprintf(buf, "%s\n", wwn); + + /* For virtual ports it's always key */ + if ((vha->vp_idx != 0) || vha->port_name_set) + res += sprintf(&buf[res], "%s\n", SCST_SYSFS_KEY_MARK); + + kfree(wwn); + +out: + return res; +} + +static ssize_t q2t_port_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buffer, size_t size) +{ + struct scst_tgt *scst_tgt; + struct q2t_tgt *tgt; + scsi_qla_host_t *vha; + u64 port_name, old_port_name; + ssize_t res = -E_TGT_PRIV_NOT_YET_SET; + + TRACE_ENTRY(); + + scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + if (!tgt) + goto out; + vha = tgt->vha; + + sBUG_ON(vha->vp_idx != 0); + + if (size == 0) + goto out_default; + + res = q2t_parse_wwn(buffer, &port_name); + if (res != 0) { + if ((buffer[0] == '\0') || (buffer[0] == '\n')) + goto out_default; + PRINT_ERROR("qla2x00t(%ld): Wrong port name", vha->host_no); + goto out; + } + + old_port_name = wwn_to_u64(vha->port_name); + if (old_port_name == port_name) + goto out_success; + + if (vha->port_name_set) + vha->port_name_set = (port_name != wwn_to_u64(vha->hw->orig_hw_port_name)); + else { + BUILD_BUG_ON(sizeof(vha->port_name) != sizeof(vha->hw->orig_hw_port_name)); + memcpy(vha->hw->orig_hw_port_name, vha->port_name, + sizeof(vha->port_name)); + vha->port_name_set = 1; + } + + u64_to_wwn(port_name, vha->port_name); + +abort: + if (qla_tgt_mode_enabled(vha)) { + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2x00_wait_for_hba_online(vha); + } + +out_success: + res = size; + +out: + TRACE_EXIT_RES(res); + return res; + +out_default: + if (vha->port_name_set) + memcpy(vha->port_name, vha->hw->orig_hw_port_name, + sizeof(vha->port_name)); + vha->port_name_set = 0; goto abort; } @@ -6583,7 +6915,7 @@ static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; - scsi_qla_host_t *ha; + scsi_qla_host_t *base_vha; ssize_t res = -E_TGT_PRIV_NOT_YET_SET; char *wwn; @@ -6591,9 +6923,9 @@ static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, tgt = scst_tgt_get_tgt_priv(scst_tgt); if (!tgt) goto out; - ha = to_qla_parent(tgt->ha); + base_vha = pci_get_drvdata(tgt->vha->hw->pdev); - res = q2t_get_target_name(ha->port_name, &wwn); + res = q2t_get_target_name(base_vha->port_name, &wwn); if (res != 0) goto out; @@ -6802,14 +7134,13 @@ static int __init q2t_init(void) goto out_kmem_free; } + q2t_target_driver_reg(&tgt2x_template); + res = scst_register_target_template(&tgt2x_template); if (res < 0) goto out_mempool_free; - /* - * qla2xxx_tgt_register_driver() happens in q2t_target_detect - * called via scst_register_target_template() - */ + qla2xxx_add_targets(); #ifdef CONFIG_SCST_PROC res = q2t_proc_log_entry_build(&tgt2x_template); diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index 57517046a..5ff441f4b 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -1,10 +1,10 @@ /* * qla2x00t.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * QLogic 22xx/23xx/24xx/25xx FC target driver. * @@ -30,7 +30,7 @@ /* Version numbers, the same as for the kernel */ #define Q2T_VERSION(a, b, c, d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d)) -#define Q2T_VERSION_CODE Q2T_VERSION(3, 1, 0, 0) +#define Q2T_VERSION_CODE Q2T_VERSION(4, 1, 0, 0) #define Q2T_VERSION_STRING "3.1.0-pre1" #define Q2T_PROC_VERSION_NAME "version" @@ -47,7 +47,7 @@ #define IMM_NTFY_PORT_LOGOUT 0x0029 #define IMM_NTFY_PORT_CONFIG 0x002A #define IMM_NTFY_GLBL_TPRLO 0x002D -#define IMM_NTFY_GLBL_LOGO 0x002E +#define IMM_NTFY_LINK_FAILURE 0x002E #define IMM_NTFY_RESOURCE 0x0034 #define IMM_NTFY_MSG_RX 0x0036 #define IMM_NTFY_SRR 0x0045 @@ -118,7 +118,7 @@ struct q2t_tgt { struct scst_tgt *scst_tgt; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; /* * To sync between IRQ handlers and q2t_target_release(). Needed, @@ -129,8 +129,7 @@ struct q2t_tgt { int datasegs_per_cmd, datasegs_per_cont; - /* Target's flags, serialized by pha->hardware_lock */ - unsigned int tgt_enable_64bit_addr:1; /* 64-bits PCI addressing enabled */ + /* Target's flags, serialized by ha->hardware_lock */ unsigned int link_reinit_iocb_pending:1; unsigned int tm_to_unknown:1; /* TM to unknown session was sent */ unsigned int sess_works_pending:1; /* there are sess_work entries */ @@ -141,17 +140,17 @@ struct q2t_tgt { */ unsigned long tgt_stop; /* the driver is being stopped */ - struct work_struct rscn_reg_work; - - /* Count of sessions referring q2t_tgt. Protected by hardware_lock. */ - int sess_count; - /* * Protected by hardware_lock. Adding new sessions (not undelete) * also protected by tgt_mutex. */ struct list_head sess_list; + struct work_struct rscn_reg_work; + + /* Count of sessions referring q2t_tgt. Protected by hardware_lock. */ + int sess_count; + /* Protected by hardware_lock */ struct list_head del_sess_list; struct delayed_work sess_del_work; @@ -174,8 +173,6 @@ struct q2t_tgt { struct work_struct srr_work; atomic_t tgt_global_resets_count; - - struct list_head tgt_list_entry; }; /* @@ -230,6 +227,12 @@ struct q2t_cmd { struct scst_cmd scst_cmd; }; +struct q2t_unknown_atio { + atio7_entry_t atio7; + scsi_qla_host_t *vha; + struct list_head unknown_atio_list_entry; +}; + struct q2t_sess_work_param { struct list_head sess_works_list_entry; diff --git a/qla2x00t/qla2x_tgt.h b/qla2x00t/qla2x_tgt.h index 2562f1178..4ce125311 100644 --- a/qla2x00t/qla2x_tgt.h +++ b/qla2x00t/qla2x_tgt.h @@ -1,10 +1,10 @@ /* * qla2x_tgt.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Additional file for the target driver support. * @@ -28,7 +28,7 @@ #include -extern request_t *qla2x00_req_pkt(scsi_qla_host_t *ha); +extern request_t *qla2x00_req_pkt(scsi_qla_host_t *vha); #ifdef CONFIG_SCSI_QLA2XXX_TARGET @@ -36,25 +36,25 @@ extern request_t *qla2x00_req_pkt(scsi_qla_host_t *ha); extern struct qla_tgt_data qla_target; -void qla_set_tgt_mode(scsi_qla_host_t *ha); -void qla_clear_tgt_mode(scsi_qla_host_t *ha); +void qla_set_tgt_mode(scsi_qla_host_t *vha); +void qla_clear_tgt_mode(scsi_qla_host_t *vha); -static inline bool qla_tgt_mode_enabled(scsi_qla_host_t *ha) +static inline bool qla_tgt_mode_enabled(scsi_qla_host_t *vha) { - return ha->host->active_mode & MODE_TARGET; + return vha->host->active_mode & MODE_TARGET; } -static inline bool qla_ini_mode_enabled(scsi_qla_host_t *ha) +static inline bool qla_ini_mode_enabled(scsi_qla_host_t *vha) { - return ha->host->active_mode & MODE_INITIATOR; + return vha->host->active_mode & MODE_INITIATOR; } -static inline void qla_reverse_ini_mode(scsi_qla_host_t *ha) +static inline void qla_reverse_ini_mode(scsi_qla_host_t *vha) { - if (ha->host->active_mode & MODE_INITIATOR) - ha->host->active_mode &= ~MODE_INITIATOR; + if (vha->host->active_mode & MODE_INITIATOR) + vha->host->active_mode &= ~MODE_INITIATOR; else - ha->host->active_mode |= MODE_INITIATOR; + vha->host->active_mode |= MODE_INITIATOR; } /********************************************************************\ @@ -72,13 +72,14 @@ static inline void qla_reverse_ini_mode(scsi_qla_host_t *ha) * then reacquire. */ static inline void -__qla2x00_send_enable_lun(scsi_qla_host_t *ha, int enable) +__qla2x00_send_enable_lun(scsi_qla_host_t *vha, int enable) { elun_entry_t *pkt; + struct qla_hw_data *ha = vha->hw; BUG_ON(IS_FWI2_CAPABLE(ha)); - pkt = (elun_entry_t *)qla2x00_req_pkt(ha); + pkt = (elun_entry_t *)qla2x00_req_pkt(vha); if (pkt != NULL) { pkt->entry_type = ENABLE_LUN_TYPE; if (enable) { @@ -90,16 +91,16 @@ __qla2x00_send_enable_lun(scsi_qla_host_t *ha, int enable) pkt->immed_notify_count = 0; pkt->timeout = 0; } - DEBUG2(printk(KERN_DEBUG + ql_dbg(ql_dbg_init, vha, 0x0077, "scsi%lu:ENABLE_LUN IOCB imm %u cmd %u timeout %u\n", - ha->host_no, pkt->immed_notify_count, - pkt->command_count, pkt->timeout)); + vha->host_no, pkt->immed_notify_count, + pkt->command_count, pkt->timeout); /* Issue command to ISP */ - qla2x00_isp_cmd(ha); + qla2x00_start_iocbs(vha, vha->req); } else - qla_clear_tgt_mode(ha); + qla_clear_tgt_mode(vha); #if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3) if (!pkt) pr_err("%s: **** FAILED ****\n", __func__); @@ -117,14 +118,15 @@ __qla2x00_send_enable_lun(scsi_qla_host_t *ha, int enable) * enable = enable/disable flag. */ static inline void -qla2x00_send_enable_lun(scsi_qla_host_t *ha, bool enable) +qla2x00_send_enable_lun(scsi_qla_host_t *vha, bool enable) { + struct qla_hw_data *ha = vha->hw; + if (!IS_FWI2_CAPABLE(ha)) { unsigned long flags; - scsi_qla_host_t *pha = to_qla_parent(ha); - spin_lock_irqsave(&pha->hardware_lock, flags); - __qla2x00_send_enable_lun(ha, enable); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); + __qla2x00_send_enable_lun(vha, enable); + spin_unlock_irqrestore(&ha->hardware_lock, flags); } } @@ -132,11 +134,32 @@ extern void qla2xxx_add_targets(void); #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED)) extern size_t -qla2xxx_add_vtarget(u64 *port_name, u64 *node_name, u64 *parent_host); -extern size_t qla2xxx_del_vtarget(u64 *port_name); +qla2xxx_add_vtarget(u64 port_name, u64 node_name, u64 parent_host); +extern size_t qla2xxx_del_vtarget(u64 port_name); #endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED))*/ +extern void qla_unknown_atio_work_fn(struct delayed_work *work); + +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ + +static inline bool qla_tgt_mode_enabled(scsi_qla_host_t *vha) +{ + return false; +} + +static inline bool qla_ini_mode_enabled(scsi_qla_host_t *vha) +{ + return true; +} + #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ +static inline bool qla_firmware_active(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + return qla_tgt_mode_enabled(base_vha) || qla_ini_mode_enabled(base_vha); +} + #endif /* __QLA2X_TGT_H */ diff --git a/qla2x00t/qla2x_tgt_def.h b/qla2x00t/qla2x_tgt_def.h index 8b0ea8454..28bf9ee47 100644 --- a/qla2x00t/qla2x_tgt_def.h +++ b/qla2x00t/qla2x_tgt_def.h @@ -1,10 +1,10 @@ /* * qla2x_tgt_def.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Additional file for the target driver support. * @@ -44,13 +44,13 @@ * Must be changed on any change in any initiator visible interfaces or * data in the target add-on */ -#define QLA2X_TARGET_MAGIC 270 +#define QLA2X_TARGET_MAGIC 1377 /* * Must be changed on any change in any target visible interfaces or * data in the initiator */ -#define QLA2X_INITIATOR_MAGIC 57225 +#define QLA2X_INITIATOR_MAGIC 59232 #define QLA2X_INI_MODE_STR_EXCLUSIVE "exclusive" #define QLA2X_INI_MODE_STR_DISABLED "disabled" @@ -148,7 +148,7 @@ typedef struct { uint16_t reserved_5; uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ uint16_t reserved_6[20]; -} __packed elun_entry_t; +} __attribute__((packed)) elun_entry_t; #define ENABLE_LUN_SUCCESS 0x01 #define ENABLE_LUN_RC_NONZERO 0x04 #define ENABLE_LUN_INVALID_REQUEST 0x06 @@ -179,7 +179,7 @@ typedef struct { uint16_t reserved_5; uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ uint16_t reserved_7[20]; -} __packed modify_lun_entry_t; +} __attribute__((packed)) modify_lun_entry_t; #define MODIFY_LUN_SUCCESS 0x01 #define MODIFY_LUN_CMD_ADD BIT_0 #define MODIFY_LUN_CMD_SUB BIT_1 @@ -219,7 +219,7 @@ typedef struct { uint16_t srr_ox_id; uint8_t reserved_2[30]; uint16_t ox_id; -} __packed notify_entry_t; +} __attribute__((packed)) notify_entry_t; #endif #ifndef NOTIFY_ACK_TYPE @@ -250,7 +250,7 @@ typedef struct { uint8_t srr_reject_code_expl; uint8_t reserved_2[26]; uint16_t ox_id; -} __packed nack_entry_t; +} __attribute__((packed)) nack_entry_t; #define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0 #define NOTIFY_ACK_SRR_FLAGS_REJECT 1 @@ -287,7 +287,7 @@ typedef struct { uint8_t initiator_port_name[WWN_SIZE]; /* on qla23xx */ uint16_t reserved_32[6]; uint16_t ox_id; -} __packed atio_entry_t; +} __attribute__((packed)) atio_entry_t; #endif #ifndef CONTINUE_TGT_IO_TYPE @@ -314,7 +314,7 @@ typedef struct { uint16_t scsi_status; uint32_t transfer_length; uint32_t dseg_0_address[0]; -} __packed ctio_common_entry_t; +} __attribute__((packed)) ctio_common_entry_t; #define ATIO_PATH_INVALID 0x07 #define ATIO_CANT_PROV_CAP 0x16 #define ATIO_CDB_VALID 0x3D @@ -333,7 +333,7 @@ typedef struct { uint32_t dseg_1_length; /* Data segment 1 length. */ uint32_t dseg_2_address; /* Data segment 2 address. */ uint32_t dseg_2_length; /* Data segment 2 length. */ -} __packed ctio_entry_t; +} __attribute__((packed)) ctio_entry_t; #define CTIO_SUCCESS 0x01 #define CTIO_ABORTED 0x02 #define CTIO_INVALID_RX_ID 0x08 @@ -371,7 +371,7 @@ typedef struct { uint16_t scsi_status; uint16_t response_length; uint8_t sense_data[26]; -} __packed ctio_ret_entry_t; +} __attribute__((packed)) ctio_ret_entry_t; #endif #define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */ @@ -389,7 +389,7 @@ typedef struct { uint16_t ox_id; uint16_t rx_id; uint32_t parameter; -} __packed fcp_hdr_t; +} __attribute__((packed)) fcp_hdr_t; typedef struct { uint8_t d_id[3]; @@ -404,7 +404,7 @@ typedef struct { uint16_t rx_id; uint16_t ox_id; uint32_t parameter; -} __packed fcp_hdr_le_t; +} __attribute__((packed)) fcp_hdr_le_t; #define F_CTL_EXCH_CONTEXT_RESP BIT_23 #define F_CTL_SEQ_CONTEXT_RESIP BIT_22 @@ -449,7 +449,7 @@ typedef struct { */ uint8_t add_cdb[4]; /* uint32_t data_length; */ -} __packed fcp_cmnd_t; +} __attribute__((packed)) fcp_cmnd_t; /* * ISP queue - Accept Target I/O (ATIO) type 7 entry for 24xx structure @@ -470,7 +470,7 @@ typedef struct { #define ATIO_EXCHANGE_ADDRESS_UNKNOWN 0xFFFFFFFF fcp_hdr_t fcp_hdr; fcp_cmnd_t fcp_cmnd; -} __packed atio7_entry_t; +} __attribute__((packed)) atio7_entry_t; #define CTIO_TYPE7 0x12 /* Continue target I/O entry (for 24xx) */ @@ -494,7 +494,7 @@ typedef struct { uint8_t initiator_id[3]; uint8_t reserved; uint32_t exchange_addr; -} __packed ctio7_common_entry_t; +} __attribute__((packed)) ctio7_common_entry_t; typedef struct { ctio7_common_entry_t common; @@ -509,7 +509,7 @@ typedef struct { uint32_t reserved3; uint32_t dseg_0_address[2]; /* Data segment 0 address. */ uint32_t dseg_0_length; /* Data segment 0 length. */ -} __packed ctio7_status0_entry_t; +} __attribute__((packed)) ctio7_status0_entry_t; typedef struct { ctio7_common_entry_t common; @@ -521,7 +521,7 @@ typedef struct { uint16_t response_len; uint16_t reserved; uint8_t sense_data[24]; -} __packed ctio7_status1_entry_t; +} __attribute__((packed)) ctio7_status1_entry_t; typedef struct { uint8_t entry_type; /* Entry type. */ @@ -542,7 +542,7 @@ typedef struct { uint16_t reserved3; uint32_t relative_offset; uint8_t reserved4[24]; -} __packed ctio7_fw_entry_t; +} __attribute__((packed)) ctio7_fw_entry_t; /* CTIO7 flags values */ #define CTIO7_FLAGS_SEND_STATUS BIT_15 @@ -574,19 +574,22 @@ typedef struct { uint16_t srr_rx_id; uint16_t status; uint8_t status_subcode; - uint8_t reserved_3; + uint8_t fw_handle; uint32_t exchange_address; uint32_t srr_rel_offs; uint16_t srr_ui; uint16_t srr_ox_id; - uint8_t reserved_4[19]; + uint8_t els_nport_id[3]; + uint8_t reserved_4; + uint16_t els_nport_handle; + uint8_t reserved_5[13]; uint8_t vp_index; - uint32_t reserved_5; + uint32_t reserved_6; uint8_t port_id[3]; - uint8_t reserved_6; - uint16_t reserved_7; + uint8_t reserved_7; + uint16_t reserved_8; uint16_t ox_id; -} __packed notify24xx_entry_t; +} __attribute__((packed)) notify24xx_entry_t; #define ELS_PLOGI 0x3 #define ELS_FLOGI 0x4 @@ -612,7 +615,7 @@ typedef struct { uint16_t srr_rx_id; uint16_t status; uint8_t status_subcode; - uint8_t reserved_3; + uint8_t fw_handle; uint32_t exchange_address; uint32_t srr_rel_offs; uint16_t srr_ui; @@ -624,7 +627,7 @@ typedef struct { uint8_t srr_reject_code; uint8_t reserved_5[7]; uint16_t ox_id; -} __packed nack24xx_entry_t; +} __attribute__((packed)) nack24xx_entry_t; /* * ISP queue - ABTS received/response entries structure definition for 24xx. @@ -652,7 +655,7 @@ typedef struct { fcp_hdr_le_t fcp_hdr_le; uint8_t reserved_4[16]; uint32_t exchange_addr_to_abort; -} __packed abts24_recv_entry_t; +} __attribute__((packed)) abts24_recv_entry_t; #define ABTS_PARAM_ABORT_SEQ BIT_0 @@ -666,7 +669,7 @@ typedef struct { uint16_t ox_id; uint16_t high_seq_cnt; uint16_t low_seq_cnt; -} __packed ba_acc_le_t; +} __attribute__((packed)) ba_acc_le_t; typedef struct { uint8_t vendor_uniq; @@ -675,7 +678,7 @@ typedef struct { #define BA_RJT_REASON_CODE_INVALID_COMMAND 0x1 #define BA_RJT_REASON_CODE_UNABLE_TO_PERFORM 0x9 uint8_t reserved; -} __packed ba_rjt_le_t; +} __attribute__((packed)) ba_rjt_le_t; typedef struct { uint8_t entry_type; /* Entry type. */ @@ -700,10 +703,10 @@ typedef struct { union { ba_acc_le_t ba_acct; ba_rjt_le_t ba_rjt; - } __packed payload; + } __attribute__((packed)) payload; uint32_t reserved_4; uint32_t exchange_addr_to_abort; -} __packed abts24_resp_entry_t; +} __attribute__((packed)) abts24_resp_entry_t; typedef struct { uint8_t entry_type; /* Entry type. */ @@ -731,7 +734,7 @@ typedef struct { #define ABTS_RESP_SUBCODE_ERR_ABORTED_EXCH_NOT_TERM 0x1E uint32_t error_subcode2; uint32_t exchange_addr_to_abort; -} __packed abts24_resp_fw_entry_t; +} __attribute__((packed)) abts24_resp_fw_entry_t; /********************************************************************\ * Type Definitions used by initiator & target halves @@ -754,6 +757,7 @@ struct qla_tgt_data { void (*tgt2x_ctio_completion)(scsi_qla_host_t *ha, uint32_t handle); void (*tgt_async_event)(uint16_t code, scsi_qla_host_t *ha, uint16_t *mailbox); + void (*tgt_try_to_dequeue_unknown_atios)(struct qla_hw_data *ha); int (*tgt_host_action)(scsi_qla_host_t *ha, qla2x_tgt_host_action_t action); void (*tgt_fc_port_added)(scsi_qla_host_t *ha, fc_port_t *fcport); diff --git a/qla2x00t/qla_attr.c b/qla2x00t/qla_attr.c index 1df02d8ee..3adcf4987 100644 --- a/qla2x00t/qla_attr.c +++ b/qla2x00t/qla_attr.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -26,7 +26,8 @@ static ssize_t qla2x00_show_class2_enabled(struct device *dev, struct device_attribute *attr, char *buffer) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; ulong max_size = PAGE_SIZE; ulong size; @@ -39,8 +40,9 @@ static ssize_t qla2x00_store_class2_enabled(struct device *dev, struct device_attribute *attr, const char *buffer, size_t size) { - struct scsi_qla_host *ha = shost_priv(class_to_shost(dev)); - scsi_qla_host_t *pha = to_qla_parent(ha); + struct scsi_qla_host *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); int reset = 0; unsigned long flags; int res = size; @@ -48,7 +50,12 @@ qla2x00_store_class2_enabled(struct device *dev, if (buffer == NULL) goto out; - spin_lock_irqsave(&pha->hardware_lock, flags); + if (vha != base_vha) { + res = -EINVAL; + goto out; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); switch (buffer[0]) { case '0': @@ -60,12 +67,12 @@ qla2x00_store_class2_enabled(struct device *dev, case '1': if (!ha->enable_class_2) { if (ha->fw_attributes & __constant_cpu_to_le32(BIT_0)) { - qla_printk(KERN_INFO, ha, "Enabling class 2 " - "operations.\n"); + printk(KERN_INFO "(%ld): Enabling class 2 " + "operations.\n", vha->host_no); ha->enable_class_2 = 1; reset = 1; } else { - qla_printk(KERN_INFO, ha, "Firmware doesn't " + printk(KERN_INFO "Firmware doesn't " "support class 2 operations.\n"); res = -EINVAL; goto out_unlock; @@ -73,24 +80,22 @@ qla2x00_store_class2_enabled(struct device *dev, } break; default: -#if defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_11) - qla_printk(KERN_ERR, ha, "%s: Requested action not understood: " - "%s\n", __func__, buffer); -#endif + printk(KERN_ERR "%s(%ld): Requested action not understood: " + "%s\n", __func__, vha->host_no, buffer); res = -EINVAL; goto out_unlock; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); if (reset) - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); out: return size; out_unlock: - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); goto out; } @@ -109,10 +114,10 @@ static ssize_t qla2x00_show_tgt_enabled(struct device *dev, struct device_attribute *attr, char *buffer) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); ssize_t size; - size = scnprintf(buffer, PAGE_SIZE, "%d\n", qla_tgt_mode_enabled(ha)); + size = scnprintf(buffer, PAGE_SIZE, "%d\n", qla_tgt_mode_enabled(vha)); return size; } @@ -121,14 +126,14 @@ static ssize_t qla2x00_store_tgt_enabled(struct device *dev, struct device_attribute *attr, const char *buffer, size_t size) { - struct scsi_qla_host *ha = shost_priv(class_to_shost(dev)); + struct scsi_qla_host *vha = shost_priv(class_to_shost(dev)); int res = size; if ((buffer == NULL) || (size == 0)) goto out; if (qla_target.tgt_host_action == NULL) { - qla_printk(KERN_INFO, ha, "%s: not acting for lack of target " + printk(KERN_INFO "%s: not acting for lack of target " "driver\n", __func__); res = -EINVAL; goto out; @@ -136,14 +141,14 @@ qla2x00_store_tgt_enabled(struct device *dev, switch (buffer[0]) { case '0': - res = qla_target.tgt_host_action(ha, DISABLE_TARGET_MODE); + res = qla_target.tgt_host_action(vha, DISABLE_TARGET_MODE); break; case '1': - res = qla_target.tgt_host_action(ha, ENABLE_TARGET_MODE); + res = qla_target.tgt_host_action(vha, ENABLE_TARGET_MODE); break; default: - qla_printk(KERN_ERR, ha, "%s: Requested action not " - "understood: %s\n", __func__, buffer); + printk(KERN_ERR "%s(%ld): Requested action not " + "understood: %s\n", __func__, vha->host_no, buffer); res = -EINVAL; goto out; } @@ -151,8 +156,10 @@ qla2x00_store_tgt_enabled(struct device *dev, if (res == 0) res = size; - if ((size > 1) && (buffer[1] == 'r')) - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + if ((size > 1) && (buffer[1] == 'r')) { + struct scsi_qla_host *base_vha = pci_get_drvdata(vha->hw->pdev); + set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); + } out: return res; @@ -167,11 +174,12 @@ static ssize_t qla2x00_show_expl_conf_enabled(struct device *dev, struct device_attribute *attr, char *buffer) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + struct scsi_qla_host *vha = shost_priv(class_to_shost(dev)); ulong max_size = PAGE_SIZE; ulong size; - size = scnprintf(buffer, max_size, "%d\n", ha->enable_explicit_conf); + size = scnprintf(buffer, max_size, "%d\n", + vha->hw->enable_explicit_conf); return size; } @@ -180,35 +188,41 @@ static ssize_t qla2x00_store_expl_conf_enabled(struct device *dev, struct device_attribute *attr, const char *buffer, size_t size) { - struct scsi_qla_host *ha = shost_priv(class_to_shost(dev)); - scsi_qla_host_t *pha = to_qla_parent(ha); + struct scsi_qla_host *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); unsigned long flags; + int old = ha->enable_explicit_conf; if (buffer == NULL) return size; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); switch (buffer[0]) { case '0': - ha->enable_explicit_conf = 0; - qla_printk(KERN_INFO, ha, "qla2xxx(%ld): explicit confirmation " - "disabled\n", ha->instance); + vha->hw->enable_explicit_conf = 0; + printk(KERN_INFO "qla2xxx(%ld): explicit confirmation " + "disabled\n", vha->host_no); break; case '1': - ha->enable_explicit_conf = 1; - qla_printk(KERN_INFO, ha, "qla2xxx(%ld): explicit confirmation " - "enabled\n", ha->instance); + vha->hw->enable_explicit_conf = 1; + printk(KERN_INFO "qla2xxx(%ld): explicit confirmation " + "enabled\n", vha->host_no); break; default: -#if defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_11) - qla_printk(KERN_ERR, ha, "%s: Requested action not understood: " - "%s\n", __func__, buffer); -#endif + printk(KERN_ERR "%s(%ld): Requested action not understood: " + "%s\n", __func__, vha->host_no, buffer); break; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (ha->enable_explicit_conf != old) { + set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_hba_online(vha); + } return size; } @@ -224,11 +238,11 @@ static ssize_t qla2x00_show_ini_mode_force_reverse(struct device *dev, struct device_attribute *attr, char *buffer) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); ulong max_size = PAGE_SIZE; ulong size; - size = scnprintf(buffer, max_size, "%x\n", ha->ini_mode_force_reverse); + size = scnprintf(buffer, max_size, "%x\n", vha->ini_mode_force_reverse); return size; } @@ -237,51 +251,50 @@ static ssize_t qla2x00_store_ini_mode_force_reverse(struct device *dev, struct device_attribute *attr, const char *buffer, size_t size) { - struct scsi_qla_host *ha = shost_priv(class_to_shost(dev)); - scsi_qla_host_t *pha = to_qla_parent(ha); + struct scsi_qla_host *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); unsigned long flags; if (buffer == NULL) return size; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); switch (buffer[0]) { case '0': - if (!ha->ini_mode_force_reverse) + if (!vha->ini_mode_force_reverse) goto out_unlock; - ha->ini_mode_force_reverse = 0; - qla_printk(KERN_INFO, ha, "qla2xxx(%ld): initiator mode force " - "reverse disabled\n", ha->instance); - qla_reverse_ini_mode(ha); + vha->ini_mode_force_reverse = 0; + printk(KERN_INFO "qla2xxx(%ld): initiator mode force " + "reverse disabled\n", vha->host_no); + qla_reverse_ini_mode(vha); break; case '1': - if (ha->ini_mode_force_reverse) + if (vha->ini_mode_force_reverse) goto out_unlock; - ha->ini_mode_force_reverse = 1; - qla_printk(KERN_INFO, ha, "qla2xxx(%ld): initiator mode force " - "reverse enabled\n", ha->instance); - qla_reverse_ini_mode(ha); + vha->ini_mode_force_reverse = 1; + printk(KERN_INFO "qla2xxx(%ld): initiator mode force " + "reverse enabled\n", vha->host_no); + qla_reverse_ini_mode(vha); break; default: -#if defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_11) - qla_printk(KERN_ERR, ha, "%s: Requested action not understood: " - "%s\n", __func__, buffer); -#endif + printk(KERN_ERR "%s(%ld): Requested action not understood: " + "%s\n", __func__, vha->host_no, buffer); break; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - qla2x00_wait_for_hba_online(ha); + set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); + qla2xxx_wake_dpc(base_vha); + qla2x00_wait_for_hba_online(vha); out: return size; out_unlock: - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); goto out; } @@ -330,68 +343,74 @@ static ssize_t qla2x00_show_port_database(struct device *dev, struct device_attribute *attr, char *buffer) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; ulong max_size = PAGE_SIZE; ulong size = 0; int rval, i; uint16_t entries; void *pmap; - int pmap_len; + int pmap_len, iter; - rval = qla2x00_get_node_name_list(ha, &pmap, &pmap_len); - if (rval != QLA_SUCCESS) { - size = scnprintf(buffer, max_size, - "qla2x00_get_node_name_list() failed %d\n", - rval); - goto next; - } + for (iter = 0; iter < 2; iter++) { + if (iter != 0) + size += scnprintf(buffer+size, max_size-size, "\n"); - size += scnprintf(buffer+size, max_size-size, - "Port Name List returned %d bytes\nL_ID WWPN\n", - pmap_len); - - if (IS_FWI2_CAPABLE(ha)) { - struct qla_port24_data *pmap24 = pmap; - - entries = pmap_len/sizeof(*pmap24); - - for (i = 0; (i < entries) && (size < max_size); ++i) { - uint64_t *wwn = (uint64_t *)pmap24[i].port_name; - if (*wwn == 0) - continue; - size += scnprintf(buffer+size, max_size-size, - "%04x %02x%02x%02x%02x%02x%02x%02x%02x\n", - le16_to_cpu(pmap24[i].loop_id), - pmap24[i].port_name[7], - pmap24[i].port_name[6], - pmap24[i].port_name[5], - pmap24[i].port_name[4], - pmap24[i].port_name[3], - pmap24[i].port_name[2], - pmap24[i].port_name[1], - pmap24[i].port_name[0]); + rval = qla2x00_get_node_name_list(vha, (iter == 0), &pmap, &pmap_len); + if (rval != QLA_SUCCESS) { + size = scnprintf(buffer, max_size, + "qla2x00_get_node_name_list() failed %d\n", + rval); + goto next; } - } else { - struct qla_port23_data *pmap2x = pmap; - entries = pmap_len/sizeof(*pmap2x); + size += scnprintf(buffer+size, max_size-size, + "Port Name List returned %d bytes%s\nL_ID WWPN\n", + pmap_len, (iter == 0) ? "" : " (no initiators)"); - for (i = 0; (i < entries) && (size < max_size); ++i) { - size += scnprintf(buffer+size, max_size-size, - "%04x %02x%02x%02x%02x%02x%02x%02x%02x\n", - le16_to_cpu(pmap2x[i].loop_id), - pmap2x[i].port_name[7], - pmap2x[i].port_name[6], - pmap2x[i].port_name[5], - pmap2x[i].port_name[4], - pmap2x[i].port_name[3], - pmap2x[i].port_name[2], - pmap2x[i].port_name[1], - pmap2x[i].port_name[0]); + if (IS_FWI2_CAPABLE(ha)) { + struct qla_port24_data *pmap24 = pmap; + + entries = pmap_len/sizeof(*pmap24); + + for (i = 0; (i < entries) && (size < max_size); ++i) { + uint64_t *wwn = (uint64_t *)pmap24[i].port_name; + if (*wwn == 0) + continue; + size += scnprintf(buffer+size, max_size-size, + "%04x %02x%02x%02x%02x%02x%02x%02x%02x\n", + le16_to_cpu(pmap24[i].loop_id), + pmap24[i].port_name[7], + pmap24[i].port_name[6], + pmap24[i].port_name[5], + pmap24[i].port_name[4], + pmap24[i].port_name[3], + pmap24[i].port_name[2], + pmap24[i].port_name[1], + pmap24[i].port_name[0]); + } + } else { + struct qla_port23_data *pmap2x = pmap; + + entries = pmap_len/sizeof(*pmap2x); + + for (i = 0; (i < entries) && (size < max_size); ++i) { + size += scnprintf(buffer+size, max_size-size, + "%04x %02x%02x%02x%02x%02x%02x%02x%02x\n", + le16_to_cpu(pmap2x[i].loop_id), + pmap2x[i].port_name[7], + pmap2x[i].port_name[6], + pmap2x[i].port_name[5], + pmap2x[i].port_name[4], + pmap2x[i].port_name[3], + pmap2x[i].port_name[2], + pmap2x[i].port_name[1], + pmap2x[i].port_name[0]); + } } - } - kfree(pmap); + kfree(pmap); + } next: if (size < max_size) { @@ -400,7 +419,7 @@ next: char *id_iter; struct gid_list_info *gid; - gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, + gid_list = dma_alloc_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), &gid_list_dma, GFP_KERNEL); if (gid_list == NULL) { size += scnprintf(buffer+size, max_size-size, @@ -409,7 +428,7 @@ next: } /* Get list of logged in devices. */ - rval = qla2x00_get_id_list(ha, gid_list, gid_list_dma, + rval = qla2x00_get_id_list(vha, gid_list, gid_list_dma, &entries); if (rval != QLA_SUCCESS) { size += scnprintf(buffer+size, max_size-size, @@ -446,8 +465,8 @@ next: id_iter += ha->gid_list_info_size; } out_free_id_list: - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, gid_list, - gid_list_dma); + dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), + gid_list, gid_list_dma); } out_id_list_failed: @@ -459,7 +478,7 @@ out_id_list_failed: size += scnprintf(buffer+size, max_size-size, "\nfc_ports database\n"); - list_for_each_entry_rcu(fcport, &ha->fcports, list) { + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (size >= max_size) goto out; switch (atomic_read(&fcport->state)) { @@ -467,9 +486,6 @@ out_id_list_failed: case FCS_DEVICE_DEAD : state = "Dead"; break; case FCS_DEVICE_LOST : state = "Lost"; break; case FCS_ONLINE : state = "Online"; break; - case FCS_NOT_SUPPORTED : state = "Not Supported"; break; - case FCS_FAILOVER : state = "Failover"; break; - case FCS_FAILOVER_FAILED : state = "Failover Failed"; break; default: state = "Unknown"; break; } @@ -492,39 +508,41 @@ out: return size; } + static ssize_t qla2x00_update_portdb(struct device *dev, struct device_attribute *attr, const char *buffer, size_t size) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); if ((buffer == NULL) || (size == 0)) goto out; switch (buffer[0]) { case '2': - qla_printk(KERN_INFO, ha, "Reconfiguring loop on %ld\n", - ha->host_no); - qla2x00_configure_loop(ha); + printk(KERN_INFO "Reconfiguring loop on %ld\n", + vha->host_no); + qla2x00_configure_loop(vha); break; case 'l': case 'L': - qla_printk(KERN_INFO, ha, "Reconfiguring local loop on %ld\n", - ha->host_no); - qla2x00_configure_local_loop(ha); + printk(KERN_INFO "Reconfiguring local loop on %ld\n", + vha->host_no); + qla2x00_configure_local_loop(vha); break; case 'f': case 'F': - qla_printk(KERN_INFO, ha, "Reconfiguring fabric on %ld\n", - ha->host_no); - qla2x00_configure_fabric(ha); + printk(KERN_INFO "Reconfiguring fabric on %ld\n", + vha->host_no); + qla2x00_configure_fabric(vha); + /* fall through */ default: - qla_printk(KERN_INFO, ha, "Resyncing loop on %ld\n", - ha->host_no); - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); + printk(KERN_INFO "Resyncing loop on %ld\n", + vha->host_no); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); break; } @@ -552,20 +570,27 @@ qla2x00_sysfs_read_fw_dump(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); - char *rbuf = (char *)ha->fw_dump; + struct qla_hw_data *ha = vha->hw; + int rval = 0; if (ha->fw_dump_reading == 0) return 0; - if (off > ha->fw_dump_len) - return 0; - if (off + count > ha->fw_dump_len) - count = ha->fw_dump_len - off; - memcpy(buf, &rbuf[off], count); - - return (count); + if (IS_QLA82XX(ha)) { + if (off < ha->md_template_size) { + rval = memory_read_from_buffer(buf, count, + &off, ha->md_tmplt_hdr, ha->md_template_size); + return rval; + } + off -= ha->md_template_size; + rval = memory_read_from_buffer(buf, count, + &off, ha->md_dump, ha->md_dump_size); + return rval; + } else + return memory_read_from_buffer(buf, count, &off, ha->fw_dump, + ha->fw_dump_len); } static ssize_t @@ -580,8 +605,9 @@ qla2x00_sysfs_write_fw_dump(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + struct qla_hw_data *ha = vha->hw; int reading; if (off != 0) @@ -593,8 +619,13 @@ qla2x00_sysfs_write_fw_dump(struct file *file, if (!ha->fw_dump_reading) break; - qla_printk(KERN_INFO, ha, - "Firmware dump cleared on (%ld).\n", ha->host_no); + ql_log(ql_log_info, vha, 0x705d, + "Firmware dump cleared on (%ld).\n", vha->host_no); + + if (IS_QLA82XX(vha->hw)) { + qla82xx_md_free(vha); + qla82xx_md_prep(vha); + } ha->fw_dump_reading = 0; ha->fw_dumped = 0; @@ -603,19 +634,38 @@ qla2x00_sysfs_write_fw_dump(struct file *file, if (ha->fw_dumped && !ha->fw_dump_reading) { ha->fw_dump_reading = 1; - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x705e, "Raw firmware dump ready for read on (%ld).\n", - ha->host_no); + vha->host_no); } break; case 2: - qla2x00_alloc_fw_dump(ha); + qla2x00_alloc_fw_dump(vha); break; case 3: - qla2x00_system_error(ha); + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla82xx_idc_unlock(ha); + } else + qla2x00_system_error(vha); + break; + case 4: + if (IS_QLA82XX(ha)) { + if (ha->md_tmplt_hdr) + ql_dbg(ql_dbg_user, vha, 0x705b, + "MiniDump supported with this firmware.\n"); + else + ql_dbg(ql_dbg_user, vha, 0x709d, + "MiniDump not supported with this firmware.\n"); + } + break; + case 5: + if (IS_QLA82XX(ha)) + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; } - return (count); + return count; } static struct bin_attribute sysfs_fw_dump_attr = { @@ -640,22 +690,18 @@ qla2x00_sysfs_read_nvram(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); - int size = ha->nvram_size; - char *nvram_cache = ha->nvram; + struct qla_hw_data *ha = vha->hw; - if (!capable(CAP_SYS_ADMIN) || off > size || count == 0) + if (!capable(CAP_SYS_ADMIN)) return 0; - if (off + count > size) { - size -= off; - count = size; - } - /* Read NVRAM data from cache. */ - memcpy(buf, &nvram_cache[off], count); - - return count; + if (IS_NOCACHE_VPD_TYPE(ha)) + ha->isp_ops->read_optrom(vha, ha->nvram, ha->flt_region_nvram << 2, + ha->nvram_size); + return memory_read_from_buffer(buf, count, &off, ha->nvram, + ha->nvram_size); } static ssize_t @@ -670,12 +716,14 @@ qla2x00_sysfs_write_nvram(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + struct qla_hw_data *ha = vha->hw; uint16_t cnt; - if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size) - return 0; + if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size || + !ha->isp_ops->write_nvram) + return -EINVAL; /* Checksum NVRAM. */ if (IS_FWI2_CAPABLE(ha)) { @@ -700,14 +748,25 @@ qla2x00_sysfs_write_nvram(struct file *file, *iter = chksum; } + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x705f, + "HBA not online, failing NVRAM update.\n"); + return -EAGAIN; + } + /* Write NVRAM. */ - ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count); - ha->isp_ops->read_nvram(ha, (uint8_t *)ha->nvram, ha->nvram_base, + ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->nvram_base, count); + ha->isp_ops->read_nvram(vha, (uint8_t *)ha->nvram, ha->nvram_base, count); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ql_dbg(ql_dbg_user, vha, 0x7060, + "Setting ISP_ABORT_NEEDED\n"); + /* NVRAM settings take effect immediately. */ + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_chip_reset(vha); - return (count); + return count; } static struct bin_attribute sysfs_nvram_attr = { @@ -732,19 +791,15 @@ qla2x00_sysfs_read_optrom(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + struct qla_hw_data *ha = vha->hw; if (ha->optrom_state != QLA_SREADING) return 0; - if (off > ha->optrom_region_size) - return 0; - if (off + count > ha->optrom_region_size) - count = ha->optrom_region_size - off; - memcpy(buf, &ha->optrom_buffer[off], count); - - return count; + return memory_read_from_buffer(buf, count, &off, ha->optrom_buffer, + ha->optrom_region_size); } static ssize_t @@ -759,8 +814,9 @@ qla2x00_sysfs_write_optrom(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + struct qla_hw_data *ha = vha->hw; if (ha->optrom_state != QLA_SWRITING) return -EINVAL; @@ -796,14 +852,19 @@ qla2x00_sysfs_write_optrom_ctl(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + struct qla_hw_data *ha = vha->hw; + uint32_t start = 0; uint32_t size = ha->optrom_size; int val, valid; if (off) - return 0; + return -EINVAL; + + if (unlikely(pci_channel_offline(ha->pdev))) + return -EAGAIN; if (sscanf(buf, "%d:%x:%x", &val, &start, &size) < 1) return -EINVAL; @@ -814,26 +875,20 @@ qla2x00_sysfs_write_optrom_ctl(struct file *file, case 0: if (ha->optrom_state != QLA_SREADING && ha->optrom_state != QLA_SWRITING) - break; + return -EINVAL; ha->optrom_state = QLA_SWAITING; - DEBUG2(qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_user, vha, 0x7061, "Freeing flash region allocation -- 0x%x bytes.\n", - ha->optrom_region_size)); + ha->optrom_region_size); vfree(ha->optrom_buffer); ha->optrom_buffer = NULL; break; case 1: if (ha->optrom_state != QLA_SWAITING) - break; - - if (start & 0xfff) { - qla_printk(KERN_WARNING, ha, - "Invalid start region 0x%x/0x%x.\n", start, size); return -EINVAL; - } ha->optrom_region_start = start; ha->optrom_region_size = start + size > ha->optrom_size ? @@ -842,25 +897,31 @@ qla2x00_sysfs_write_optrom_ctl(struct file *file, ha->optrom_state = QLA_SREADING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); if (ha->optrom_buffer == NULL) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7062, "Unable to allocate memory for optrom retrieval " "(%x).\n", ha->optrom_region_size); ha->optrom_state = QLA_SWAITING; - return count; + return -ENOMEM; } - DEBUG2(qla_printk(KERN_INFO, ha, + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7063, + "HBA not online, failing NVRAM update.\n"); + return -EAGAIN; + } + + ql_dbg(ql_dbg_user, vha, 0x7064, "Reading flash region -- 0x%x/0x%x.\n", - ha->optrom_region_start, ha->optrom_region_size)); + ha->optrom_region_start, ha->optrom_region_size); memset(ha->optrom_buffer, 0, ha->optrom_region_size); - ha->isp_ops->read_optrom(ha, ha->optrom_buffer, + ha->isp_ops->read_optrom(vha, ha->optrom_buffer, ha->optrom_region_start, ha->optrom_region_size); break; case 2: if (ha->optrom_state != QLA_SWAITING) - break; + return -EINVAL; /* * We need to be more restrictive on which FLASH regions are @@ -885,13 +946,14 @@ qla2x00_sysfs_write_optrom_ctl(struct file *file, valid = 0; if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0) valid = 1; - else if (start == (FA_BOOT_CODE_ADDR*4) || - start == (FA_RISC_CODE_ADDR*4)) + else if (start == (ha->flt_region_boot * 4) || + start == (ha->flt_region_fw * 4)) valid = 1; - else if (IS_QLA25XX(ha) && start == (FA_VPD_NVRAM_ADDR*4)) + else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) + || IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) valid = 1; if (!valid) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7065, "Invalid start region 0x%x/0x%x.\n", start, size); return -EINVAL; } @@ -903,33 +965,39 @@ qla2x00_sysfs_write_optrom_ctl(struct file *file, ha->optrom_state = QLA_SWRITING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); if (ha->optrom_buffer == NULL) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7066, "Unable to allocate memory for optrom update " - "(%x).\n", ha->optrom_region_size); + "(%x)\n", ha->optrom_region_size); ha->optrom_state = QLA_SWAITING; - return count; + return -ENOMEM; } - DEBUG2(qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_user, vha, 0x7067, "Staging flash region write -- 0x%x/0x%x.\n", - ha->optrom_region_start, ha->optrom_region_size)); + ha->optrom_region_start, ha->optrom_region_size); memset(ha->optrom_buffer, 0, ha->optrom_region_size); break; case 3: if (ha->optrom_state != QLA_SWRITING) - break; + return -EINVAL; - DEBUG2(qla_printk(KERN_INFO, ha, + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7068, + "HBA not online, failing flash update.\n"); + return -EAGAIN; + } + + ql_dbg(ql_dbg_user, vha, 0x7069, "Writing flash region -- 0x%x/0x%x.\n", - ha->optrom_region_start, ha->optrom_region_size)); + ha->optrom_region_start, ha->optrom_region_size); - ha->isp_ops->write_optrom(ha, ha->optrom_buffer, + ha->isp_ops->write_optrom(vha, ha->optrom_buffer, ha->optrom_region_start, ha->optrom_region_size); break; default: - count = -EINVAL; + return -EINVAL; } return count; } @@ -955,22 +1023,20 @@ qla2x00_sysfs_read_vpd(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); - int size = ha->vpd_size; - char *vpd_cache = ha->vpd; + struct qla_hw_data *ha = vha->hw; - if (!capable(CAP_SYS_ADMIN) || off > size || count == 0) - return 0; - if (off + count > size) { - size -= off; - count = size; - } + if (unlikely(pci_channel_offline(ha->pdev))) + return -EAGAIN; - /* Read NVRAM data from cache. */ - memcpy(buf, &vpd_cache[off], count); + if (!capable(CAP_SYS_ADMIN)) + return -EINVAL; - return count; + if (IS_NOCACHE_VPD_TYPE(ha)) + ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_vpd << 2, + ha->vpd_size); + return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size); } static ssize_t @@ -985,15 +1051,40 @@ qla2x00_sysfs_write_vpd(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + struct qla_hw_data *ha = vha->hw; + uint8_t *tmp_data; - if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->vpd_size) + if (unlikely(pci_channel_offline(ha->pdev))) return 0; + if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->vpd_size || + !ha->isp_ops->write_nvram) + return 0; + + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x706a, + "HBA not online, failing VPD update.\n"); + return -EAGAIN; + } + /* Write NVRAM. */ - ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->vpd_base, count); - ha->isp_ops->read_nvram(ha, (uint8_t *)ha->vpd, ha->vpd_base, count); + ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->vpd_base, count); + ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd, ha->vpd_base, count); + + /* Update flash version information for 4Gb & above. */ + if (!IS_FWI2_CAPABLE(ha)) + return -EINVAL; + + tmp_data = vmalloc(256); + if (!tmp_data) { + ql_log(ql_log_warn, vha, 0x706b, + "Unable to allocate memory for VPD information update.\n"); + return -ENOMEM; + } + ha->isp_ops->get_flash_version(vha, tmp_data); + vfree(tmp_data); return count; } @@ -1020,8 +1111,9 @@ qla2x00_sysfs_read_sfp(struct file *file, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + struct qla_hw_data *ha = vha->hw; uint16_t iter, addr, offset; int rval; @@ -1034,7 +1126,7 @@ qla2x00_sysfs_read_sfp(struct file *file, ha->sfp_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &ha->sfp_data_dma); if (!ha->sfp_data) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x706c, "Unable to allocate memory for SFP read-data.\n"); return 0; } @@ -1050,14 +1142,14 @@ do_read: offset = 0; } - rval = qla2x00_read_sfp(ha, ha->sfp_data_dma, addr, offset, - SFP_BLOCK_SIZE); + rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, ha->sfp_data, + addr, offset, SFP_BLOCK_SIZE, 0); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x706d, "Unable to read SFP data (%x/%x/%x).\n", rval, addr, offset); - count = 0; - break; + + return -EIO; } memcpy(buf, ha->sfp_data, SFP_BLOCK_SIZE); buf += SFP_BLOCK_SIZE; @@ -1075,211 +1167,198 @@ static struct bin_attribute sysfs_sfp_attr = { .read = qla2x00_sysfs_read_sfp, }; -static void -qla2x00_wait_for_passthru_completion(struct scsi_qla_host *ha) -{ - unsigned long timeout; - - if (unlikely(pci_channel_offline(ha->pdev))) - return; - - timeout = ((ha->r_a_tov / 10 * 2) + 5) * HZ; - if (!wait_for_completion_timeout(&ha->pass_thru_intr_comp, timeout)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru request timed out.\n")); - if (IS_QLA82XX(ha)) - set_bit(FCOE_CTX_RESET_NEEDED, &ha->dpc_flags); - else { - ha->isp_ops->fw_dump(ha, 0); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - } - qla2xxx_wake_dpc(ha); - ha->pass_thru_cmd_result = 0; - ha->pass_thru_cmd_in_process = 0; - } -} - static ssize_t -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \ - (!defined(RHEL_RELEASE_CODE) || \ - RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 2)) -qla2x00_sysfs_read_ct( +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +qla2x00_sysfs_write_reset( #else -qla2x00_sysfs_read_ct(struct file *file, +qla2x00_sysfs_write_reset(struct file *file, #endif - struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, - loff_t off, size_t count) + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, - struct device, kobj))); + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + int type; - if (!ha->pass_thru_cmd_in_process || !ha->pass_thru_cmd_result) { - DEBUG3(qla_printk(KERN_WARNING, ha, - "Passthru CT response is not available.\n")); - return 0; + if (off != 0) + return -EINVAL; + + type = simple_strtol(buf, NULL, 10); + switch (type) { + case 0x2025c: + ql_log(ql_log_info, vha, 0x706e, + "Issuing ISP reset.\n"); + + scsi_block_requests(vha->host); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + if (IS_QLA82XX(ha)) { + ha->flags.isp82xx_no_md_cap = 1; + qla82xx_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla82xx_idc_unlock(ha); + } + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_chip_reset(vha); + scsi_unblock_requests(vha->host); + break; + case 0x2025d: + if (!IS_QLA81XX(ha) || !IS_QLA8031(ha)) + return -EPERM; + + ql_log(ql_log_info, vha, 0x706f, + "Issuing MPI reset.\n"); + + /* Make sure FC side is not in reset */ + qla2x00_wait_for_hba_online(vha); + + /* Issue MPI reset */ + scsi_block_requests(vha->host); + if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0x7070, + "MPI reset failed.\n"); + scsi_unblock_requests(vha->host); + break; + case 0x2025e: + if (!IS_QLA82XX(ha) || vha != base_vha) { + ql_log(ql_log_info, vha, 0x7071, + "FCoE ctx reset no supported.\n"); + return -EPERM; + } + + ql_log(ql_log_info, vha, 0x7072, + "Issuing FCoE ctx reset.\n"); + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_fcoe_ctx_reset(vha); + break; } - - memcpy(buf, ha->pass_thru, count); - - ha->pass_thru_cmd_result = 0; - ha->pass_thru_cmd_in_process = 0; - return count; } +static struct bin_attribute sysfs_reset_attr = { + .attr = { + .name = "reset", + .mode = S_IWUSR, + }, + .size = 0, + .write = qla2x00_sysfs_write_reset, +}; + static ssize_t -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \ - (!defined(RHEL_RELEASE_CODE) || \ - RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 2)) -qla2x00_sysfs_write_ct( +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +qla2x00_sysfs_read_xgmac_stats( #else -qla2x00_sysfs_write_ct(struct file *file, +qla2x00_sysfs_read_xgmac_stats(struct file *file, #endif struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, - loff_t off, size_t count) + char *buf, loff_t off, size_t count) { - struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, - struct device, kobj))); - fc_ct_request_t *request = (void *)buf; - struct ct_entry_24xx *ct_iocb = NULL; - ms_iocb_entry_t *ct_iocb_2G = NULL; - unsigned long flags; + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + struct qla_hw_data *ha = vha->hw; + int rval; + uint16_t actual_size; - if (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || - test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || - test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) { - DEBUG2_3_11(qla_printk(KERN_INFO, ha, - "%s(%ld): isp reset in progress.\n", - __func__, ha->host_no)); - goto ct_error0; - } - if (atomic_read(&ha->loop_state) != LOOP_READY) - goto ct_error0; - if (count < sizeof(request->ct_iu)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru CT buffer insufficient size %zu...\n", count)); - goto ct_error0; - } - if (ha->pass_thru_cmd_in_process || ha->pass_thru_cmd_result) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru CT request is already progress\n")); - goto ct_error0; - } - if (qla2x00_mgmt_svr_login(ha)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru CT request failed to login management server\n")); - goto ct_error0; + if (!capable(CAP_SYS_ADMIN) || off != 0 || count > XGMAC_DATA_SIZE) + return 0; + + if (ha->xgmac_data) + goto do_read; + + ha->xgmac_data = dma_alloc_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE, + &ha->xgmac_data_dma, GFP_KERNEL); + if (!ha->xgmac_data) { + ql_log(ql_log_warn, vha, 0x7076, + "Unable to allocate memory for XGMAC read-data.\n"); + return 0; } - ha->pass_thru_cmd_in_process = 1; - spin_lock_irqsave(&ha->hardware_lock, flags); +do_read: + actual_size = 0; + memset(ha->xgmac_data, 0, XGMAC_DATA_SIZE); - if (count > PAGE_SIZE) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru CT request excessive size %d...\n", - (int)count)); - count = PAGE_SIZE; + rval = qla2x00_get_xgmac_stats(vha, ha->xgmac_data_dma, + XGMAC_DATA_SIZE, &actual_size); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7077, + "Unable to read XGMAC data (%x).\n", rval); + count = 0; } - memset(ha->pass_thru, 0, PAGE_SIZE); - memcpy(ha->pass_thru, &request->ct_iu, count); - - if (IS_FWI2_CAPABLE(ha)) { - ct_iocb = (void *)qla2x00_req_pkt(ha); - - if (ct_iocb == NULL) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru CT request failed to get request " - "packet\n")); - goto ct_error1; - } - - ct_iocb->entry_type = CT_IOCB_TYPE; - ct_iocb->entry_count = 1; - ct_iocb->entry_status = 0; - ct_iocb->comp_status = __constant_cpu_to_le16(0); - if (*(buf+4) & 0xfc) - ct_iocb->nport_handle = __constant_cpu_to_le16(NPH_SNS); - else - ct_iocb->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id); - ct_iocb->cmd_dsd_count = __constant_cpu_to_le16(1); - ct_iocb->vp_index = ha->vp_idx; - ct_iocb->timeout = (cpu_to_le16(ha->r_a_tov / 10 * 2) + 2); - ct_iocb->rsp_dsd_count = __constant_cpu_to_le16(1); - ct_iocb->rsp_byte_count = cpu_to_le32(PAGE_SIZE); - ct_iocb->cmd_byte_count = cpu_to_le32(count); - - ct_iocb->dseg_0_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); - ct_iocb->dseg_0_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); - ct_iocb->dseg_0_len = ct_iocb->cmd_byte_count; - - ct_iocb->dseg_1_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); - ct_iocb->dseg_1_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); - ct_iocb->dseg_1_len = ct_iocb->rsp_byte_count; - } else { - ct_iocb_2G = (void *)qla2x00_req_pkt(ha); - - if (ct_iocb_2G == NULL) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru CT request failed to get request " - "packet\n")); - goto ct_error1; - } - - ct_iocb_2G->entry_type = CT_IOCB_TYPE; - ct_iocb_2G->entry_count = 1; - ct_iocb_2G->entry_status = 0; - SET_TARGET_ID(ha, ct_iocb_2G->loop_id, ha->mgmt_svr_loop_id); - ct_iocb_2G->status = __constant_cpu_to_le16(0); - ct_iocb_2G->control_flags = __constant_cpu_to_le16(0); - ct_iocb_2G->timeout = (cpu_to_le16(ha->r_a_tov / 10 * 2) + 2); - ct_iocb_2G->cmd_dsd_count = __constant_cpu_to_le16(1); - ct_iocb_2G->total_dsd_count = __constant_cpu_to_le16(2); - ct_iocb_2G->rsp_bytecount = cpu_to_le32(PAGE_SIZE); - ct_iocb_2G->req_bytecount = cpu_to_le32(count); - - ct_iocb_2G->dseg_req_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); - ct_iocb_2G->dseg_req_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); - ct_iocb_2G->dseg_req_length = ct_iocb_2G->req_bytecount; - - ct_iocb_2G->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); - ct_iocb_2G->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); - ct_iocb_2G->dseg_rsp_length = ct_iocb_2G->rsp_bytecount; - } - - wmb(); - qla2x00_isp_cmd(ha); - - spin_unlock_irqrestore(&ha->hardware_lock, flags); - - qla2x00_wait_for_passthru_completion(ha); + count = actual_size > count ? count: actual_size; + memcpy(buf, ha->xgmac_data, count); return count; - -ct_error1: - ha->pass_thru_cmd_in_process = 0; - spin_unlock_irqrestore(&ha->hardware_lock, flags); - -ct_error0: - DEBUG3(qla_printk(KERN_WARNING, ha, - "Passthru CT failed on scsi(%ld)\n", ha->host_no)); - return 0; } -static struct bin_attribute sysfs_ct_attr = { +static struct bin_attribute sysfs_xgmac_stats_attr = { .attr = { - .name = "ct", - .mode = S_IRUSR | S_IWUSR, + .name = "xgmac_stats", + .mode = S_IRUSR, }, .size = 0, - .read = qla2x00_sysfs_read_ct, - .write = qla2x00_sysfs_write_ct, + .read = qla2x00_sysfs_read_xgmac_stats, }; +static ssize_t +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +qla2x00_sysfs_read_dcbx_tlv( +#else +qla2x00_sysfs_read_dcbx_tlv(struct file *file, +#endif + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + struct qla_hw_data *ha = vha->hw; + int rval; + uint16_t actual_size; + + if (!capable(CAP_SYS_ADMIN) || off != 0 || count > DCBX_TLV_DATA_SIZE) + return 0; + + if (ha->dcbx_tlv) + goto do_read; + + ha->dcbx_tlv = dma_alloc_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE, + &ha->dcbx_tlv_dma, GFP_KERNEL); + if (!ha->dcbx_tlv) { + ql_log(ql_log_warn, vha, 0x7078, + "Unable to allocate memory for DCBX TLV read-data.\n"); + return -ENOMEM; + } + +do_read: + actual_size = 0; + memset(ha->dcbx_tlv, 0, DCBX_TLV_DATA_SIZE); + + rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma, + DCBX_TLV_DATA_SIZE); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7079, + "Unable to read DCBX TLV (%x).\n", rval); + return -EIO; + } + + memcpy(buf, ha->dcbx_tlv, count); + + return count; +} + +static struct bin_attribute sysfs_dcbx_tlv_attr = { + .attr = { + .name = "dcbx_tlv", + .mode = S_IRUSR, + }, + .size = 0, + .read = qla2x00_sysfs_read_dcbx_tlv, +}; static struct sysfs_entry { char *name; @@ -1292,46 +1371,61 @@ static struct sysfs_entry { { "optrom_ctl", &sysfs_optrom_ctl_attr, }, { "vpd", &sysfs_vpd_attr, 1 }, { "sfp", &sysfs_sfp_attr, 1 }, - { "ct", &sysfs_ct_attr, }, + { "reset", &sysfs_reset_attr, }, + { "xgmac_stats", &sysfs_xgmac_stats_attr, 3 }, + { "dcbx_tlv", &sysfs_dcbx_tlv_attr, 3 }, { NULL }, }; void -qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha) +qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha) { - struct Scsi_Host *host = ha->host; + struct Scsi_Host *host = vha->host; struct sysfs_entry *iter; int ret; for (iter = bin_file_entries; iter->name; iter++) { - if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha)) + if (iter->is4GBp_only && !IS_FWI2_CAPABLE(vha->hw)) + continue; + if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw)) + continue; + if (iter->is4GBp_only == 3 && !(IS_CNA_CAPABLE(vha->hw))) continue; ret = sysfs_create_bin_file(&host->shost_gendev.kobj, iter->attr); if (ret) - qla_printk(KERN_INFO, ha, - "Unable to create sysfs %s binary attribute " - "(%d).\n", iter->name, ret); + ql_log(ql_log_warn, vha, 0x00f3, + "Unable to create sysfs %s binary attribute (%d).\n", + iter->name, ret); + else + ql_dbg(ql_dbg_init, vha, 0x00f4, + "Successfully created sysfs %s binary attribure.\n", + iter->name); } } void -qla2x00_free_sysfs_attr(scsi_qla_host_t *ha) +qla2x00_free_sysfs_attr(scsi_qla_host_t *vha) { - struct Scsi_Host *host = ha->host; + struct Scsi_Host *host = vha->host; struct sysfs_entry *iter; + struct qla_hw_data *ha = vha->hw; for (iter = bin_file_entries; iter->name; iter++) { if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha)) continue; + if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha)) + continue; + if (iter->is4GBp_only == 3 && !(IS_CNA_CAPABLE(vha->hw))) + continue; sysfs_remove_bin_file(&host->shost_gendev.kobj, iter->attr); } if (ha->beacon_blink_led == 1) - ha->isp_ops->beacon_off(ha); + ha->isp_ops->beacon_off(vha); } /* Scsi_Host attributes. */ @@ -1347,22 +1441,26 @@ static ssize_t qla2x00_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; char fw_str[128]; return scnprintf(buf, PAGE_SIZE, "%s\n", - ha->isp_ops->fw_version_str(ha, fw_str, sizeof(fw_str))); + ha->isp_ops->fw_version_str(vha, fw_str, sizeof(fw_str))); } static ssize_t qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; uint32_t sn; - if (IS_FWI2_CAPABLE(ha)) - return scnprintf(buf, PAGE_SIZE, "\n"); + if (IS_FWI2_CAPABLE(ha)) { + qla2xxx_get_vpd_field(vha, "SN", buf, PAGE_SIZE); + return scnprintf(buf, PAGE_SIZE, "%s\n", buf); + } sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1; return scnprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000, @@ -1373,15 +1471,16 @@ static ssize_t qla2x00_isp_name_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return scnprintf(buf, PAGE_SIZE, "ISP%04X\n", ha->pdev->device); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + return scnprintf(buf, PAGE_SIZE, "ISP%04X\n", vha->hw->pdev->device); } static ssize_t qla2x00_isp_id_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; return scnprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n", ha->product_id[0], ha->product_id[1], ha->product_id[2], ha->product_id[3]); @@ -1391,43 +1490,44 @@ static ssize_t qla2x00_model_name_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return scnprintf(buf, PAGE_SIZE, "%s\n", ha->model_number); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", vha->hw->model_number); } static ssize_t qla2x00_model_desc_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); return scnprintf(buf, PAGE_SIZE, "%s\n", - ha->model_desc ? ha->model_desc: ""); + vha->hw->model_desc ? vha->hw->model_desc : ""); } static ssize_t qla2x00_pci_info_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); char pci_info[30]; return scnprintf(buf, PAGE_SIZE, "%s\n", - ha->isp_ops->pci_info_str(ha, pci_info, sizeof(pci_info))); + vha->hw->isp_ops->pci_info_str(vha, pci_info, sizeof(pci_info))); } static ssize_t qla2x00_link_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; int len = 0; - if (atomic_read(&ha->loop_state) == LOOP_DOWN || - atomic_read(&ha->loop_state) == LOOP_DEAD) + if (atomic_read(&vha->loop_state) == LOOP_DOWN || + atomic_read(&vha->loop_state) == LOOP_DEAD || + vha->device_flags & DFLG_NO_CABLE) len = scnprintf(buf, PAGE_SIZE, "Link Down\n"); - else if (atomic_read(&ha->loop_state) != LOOP_READY || - test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || - test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) + else if (atomic_read(&vha->loop_state) != LOOP_READY || + qla2x00_reset_active(vha)) len = scnprintf(buf, PAGE_SIZE, "Unknown Link State\n"); else { len = scnprintf(buf, PAGE_SIZE, "Link Up - "); @@ -1458,10 +1558,10 @@ static ssize_t qla2x00_zio_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); int len = 0; - switch (ha->zio_mode) { + switch (vha->hw->zio_mode) { case QLA_ZIO_MODE_6: len += scnprintf(buf + len, PAGE_SIZE-len, "Mode 6\n"); break; @@ -1476,7 +1576,8 @@ static ssize_t qla2x00_zio_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; int val = 0; uint16_t zio_mode; @@ -1494,7 +1595,7 @@ qla2x00_zio_store(struct device *dev, struct device_attribute *attr, /* Update per-hba values and queue a reset. */ if (zio_mode != QLA_ZIO_DISABLED || ha->zio_mode != QLA_ZIO_DISABLED) { ha->zio_mode = zio_mode; - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); } return strlen(buf); } @@ -1503,16 +1604,16 @@ static ssize_t qla2x00_zio_timer_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - return scnprintf(buf, PAGE_SIZE, "%d us\n", ha->zio_timer * 100); + return scnprintf(buf, PAGE_SIZE, "%d us\n", vha->hw->zio_timer * 100); } static ssize_t qla2x00_zio_timer_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); int val = 0; uint16_t zio_timer; @@ -1522,7 +1623,7 @@ qla2x00_zio_timer_store(struct device *dev, struct device_attribute *attr, return -ERANGE; zio_timer = (uint16_t)(val / 100); - ha->zio_timer = zio_timer; + vha->hw->zio_timer = zio_timer; return strlen(buf); } @@ -1531,10 +1632,10 @@ static ssize_t qla2x00_beacon_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); int len = 0; - if (ha->beacon_blink_led) + if (vha->hw->beacon_blink_led) len += scnprintf(buf + len, PAGE_SIZE-len, "Enabled\n"); else len += scnprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); @@ -1545,15 +1646,16 @@ static ssize_t qla2x00_beacon_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; int val = 0; int rval; if (IS_QLA2100(ha) || IS_QLA2200(ha)) return -EPERM; - if (test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) { - qla_printk(KERN_WARNING, ha, + if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { + ql_log(ql_log_warn, vha, 0x707a, "Abort ISP active -- ignoring beacon request.\n"); return -EBUSY; } @@ -1562,9 +1664,9 @@ qla2x00_beacon_store(struct device *dev, struct device_attribute *attr, return -EINVAL; if (val) - rval = ha->isp_ops->beacon_on(ha); + rval = ha->isp_ops->beacon_on(vha); else - rval = ha->isp_ops->beacon_off(ha); + rval = ha->isp_ops->beacon_off(vha); if (rval != QLA_SUCCESS) count = 0; @@ -1576,8 +1678,8 @@ static ssize_t qla2x00_optrom_bios_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1], ha->bios_revision[0]); } @@ -1586,8 +1688,8 @@ static ssize_t qla2x00_optrom_efi_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1], ha->efi_revision[0]); } @@ -1596,8 +1698,8 @@ static ssize_t qla2x00_optrom_fcode_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1], ha->fcode_revision[0]); } @@ -1606,13 +1708,177 @@ static ssize_t qla2x00_optrom_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n", ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2], ha->fw_revision[3]); } +static ssize_t +qla2x00_optrom_gold_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%d)\n", + ha->gold_fw_version[0], ha->gold_fw_version[1], + ha->gold_fw_version[2], ha->gold_fw_version[3]); +} + +static ssize_t +qla2x00_total_isp_aborts_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", + vha->qla_stats.total_isp_aborts); +} + +static ssize_t +qla24xx_84xx_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rval = QLA_SUCCESS; + uint16_t status[2] = {0, 0}; + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA84XX(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + if (ha->cs84xx->op_fw_version == 0) + rval = qla84xx_verify_chip(vha, status); + + if ((rval == QLA_SUCCESS) && (status[0] == 0)) + return scnprintf(buf, PAGE_SIZE, "%u\n", + (uint32_t)ha->cs84xx->op_fw_version); + + return scnprintf(buf, PAGE_SIZE, "\n"); +} + +static ssize_t +qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n", + ha->mpi_version[0], ha->mpi_version[1], ha->mpi_version[2], + ha->mpi_capabilities); +} + +static ssize_t +qla2x00_phy_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n", + ha->phy_version[0], ha->phy_version[1], ha->phy_version[2]); +} + +static ssize_t +qla2x00_flash_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size); +} + +static ssize_t +qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + if (!IS_CNA_CAPABLE(vha->hw)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id); +} + +static ssize_t +qla2x00_vn_port_mac_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + if (!IS_CNA_CAPABLE(vha->hw)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", + vha->fcoe_vn_port_mac[5], vha->fcoe_vn_port_mac[4], + vha->fcoe_vn_port_mac[3], vha->fcoe_vn_port_mac[2], + vha->fcoe_vn_port_mac[1], vha->fcoe_vn_port_mac[0]); +} + +static ssize_t +qla2x00_fabric_param_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + return scnprintf(buf, PAGE_SIZE, "%d\n", vha->hw->switch_cap); +} + +static ssize_t +qla2x00_thermal_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + int rval = QLA_FUNCTION_FAILED; + uint16_t temp, frac; + + if (!vha->hw->flags.thermal_supported) + return scnprintf(buf, PAGE_SIZE, "\n"); + + temp = frac = 0; + if (qla2x00_reset_active(vha)) + ql_log(ql_log_warn, vha, 0x707b, + "ISP reset active.\n"); + else if (!vha->hw->flags.eeh_busy) + rval = qla2x00_get_thermal_temp(vha, &temp, &frac); + if (rval != QLA_SUCCESS) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", temp, frac); +} + +static ssize_t +qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + int rval = QLA_FUNCTION_FAILED; + uint16_t state[5]; + + if (qla2x00_reset_active(vha)) + ql_log(ql_log_warn, vha, 0x707c, + "ISP reset active.\n"); + else if (!vha->hw->flags.eeh_busy) + rval = qla2x00_get_firmware_state(vha, state); + if (rval != QLA_SUCCESS) + memset(state, -1, sizeof(state)); + + return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x\n", state[0], + state[1], state[2], state[3], state[4]); +} + static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); @@ -1635,6 +1901,22 @@ static DEVICE_ATTR(optrom_fcode_version, S_IRUGO, qla2x00_optrom_fcode_version_show, NULL); static DEVICE_ATTR(optrom_fw_version, S_IRUGO, qla2x00_optrom_fw_version_show, NULL); +static DEVICE_ATTR(optrom_gold_fw_version, S_IRUGO, + qla2x00_optrom_gold_fw_version_show, NULL); +static DEVICE_ATTR(84xx_fw_version, S_IRUGO, qla24xx_84xx_fw_version_show, + NULL); +static DEVICE_ATTR(total_isp_aborts, S_IRUGO, qla2x00_total_isp_aborts_show, + NULL); +static DEVICE_ATTR(mpi_version, S_IRUGO, qla2x00_mpi_version_show, NULL); +static DEVICE_ATTR(phy_version, S_IRUGO, qla2x00_phy_version_show, NULL); +static DEVICE_ATTR(flash_block_size, S_IRUGO, qla2x00_flash_block_size_show, + NULL); +static DEVICE_ATTR(vlan_id, S_IRUGO, qla2x00_vlan_id_show, NULL); +static DEVICE_ATTR(vn_port_mac_address, S_IRUGO, + qla2x00_vn_port_mac_address_show, NULL); +static DEVICE_ATTR(fabric_param, S_IRUGO, qla2x00_fabric_param_show, NULL); +static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL); +static DEVICE_ATTR(thermal_temp, S_IRUGO, qla2x00_thermal_temp_show, NULL); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -1653,6 +1935,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_optrom_efi_version, &dev_attr_optrom_fcode_version, &dev_attr_optrom_fw_version, + &dev_attr_84xx_fw_version, &dev_attr_class2_enabled, #ifdef CONFIG_SCSI_QLA2XXX_TARGET #ifdef CONFIG_SCST_PROC @@ -1663,6 +1946,16 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_resource_counts, &dev_attr_port_database, #endif + &dev_attr_total_isp_aborts, + &dev_attr_mpi_version, + &dev_attr_phy_version, + &dev_attr_flash_block_size, + &dev_attr_vlan_id, + &dev_attr_vn_port_mac_address, + &dev_attr_fabric_param, + &dev_attr_fw_state, + &dev_attr_optrom_gold_fw_version, + &dev_attr_thermal_temp, NULL, }; @@ -1671,16 +1964,17 @@ struct device_attribute *qla2x00_host_attrs[] = { static void qla2x00_get_host_port_id(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = shost_priv(shost); + scsi_qla_host_t *vha = shost_priv(shost); - fc_host_port_id(shost) = ha->d_id.b.domain << 16 | - ha->d_id.b.area << 8 | ha->d_id.b.al_pa; + fc_host_port_id(shost) = vha->d_id.b.domain << 16 | + vha->d_id.b.area << 8 | vha->d_id.b.al_pa; } static void qla2x00_get_host_speed(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost)); + struct qla_hw_data *ha = ((struct scsi_qla_host *) + (shost_priv(shost)))->hw; u32 speed = FC_PORTSPEED_UNKNOWN; switch (ha->link_data_rate) { @@ -1696,6 +1990,12 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) case PORT_SPEED_8GB: speed = FC_PORTSPEED_8GBIT; break; + case PORT_SPEED_10GB: + speed = FC_PORTSPEED_10GBIT; + break; + case PORT_SPEED_16GB: + speed = FC_PORTSPEED_16GBIT; + break; } fc_host_speed(shost) = speed; } @@ -1703,14 +2003,14 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) static void qla2x00_get_host_port_type(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = shost_priv(shost); + scsi_qla_host_t *vha = shost_priv(shost); uint32_t port_type = FC_PORTTYPE_UNKNOWN; - if (ha->parent) { + if (vha->vp_idx) { fc_host_port_type(shost) = FC_PORTTYPE_NPIV; return; } - switch (ha->current_topology) { + switch (vha->hw->current_topology) { case ISP_CFG_NL: port_type = FC_PORTTYPE_LPORT; break; @@ -1731,11 +2031,11 @@ static void qla2x00_get_starget_node_name(struct scsi_target *starget) { struct Scsi_Host *host = dev_to_shost(starget->dev.parent); - scsi_qla_host_t *ha = shost_priv(host); + scsi_qla_host_t *vha = shost_priv(host); fc_port_t *fcport; u64 node_name = 0; - list_for_each_entry_rcu(fcport, &ha->fcports, list) { + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (fcport->rport && starget->id == fcport->rport->scsi_target_id) { node_name = wwn_to_u64(fcport->node_name); @@ -1750,11 +2050,11 @@ static void qla2x00_get_starget_port_name(struct scsi_target *starget) { struct Scsi_Host *host = dev_to_shost(starget->dev.parent); - scsi_qla_host_t *ha = shost_priv(host); + scsi_qla_host_t *vha = shost_priv(host); fc_port_t *fcport; u64 port_name = 0; - list_for_each_entry_rcu(fcport, &ha->fcports, list) { + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (fcport->rport && starget->id == fcport->rport->scsi_target_id) { port_name = wwn_to_u64(fcport->port_name); @@ -1769,11 +2069,11 @@ static void qla2x00_get_starget_port_id(struct scsi_target *starget) { struct Scsi_Host *host = dev_to_shost(starget->dev.parent); - scsi_qla_host_t *ha = shost_priv(host); + scsi_qla_host_t *vha = shost_priv(host); fc_port_t *fcport; uint32_t port_id = ~0U; - list_for_each_entry_rcu(fcport, &ha->fcports, list) { + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (fcport->rport && starget->id == fcport->rport->scsi_target_id) { port_id = fcport->d_id.b.domain << 16 | @@ -1799,11 +2099,14 @@ qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) { struct Scsi_Host *host = rport_to_shost(rport); fc_port_t *fcport = *(fc_port_t **)rport->dd_data; + unsigned long flags; if (!fcport) return; - qla2x00_abort_fcport_cmds(fcport); + /* Now that the rport has been deleted, set the fcport state to + FCS_DEVICE_DEAD */ + qla2x00_set_fcport_state(fcport, FCS_DEVICE_DEAD); /* * Transport has effectively 'deleted' the rport, clear @@ -1811,12 +2114,20 @@ qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) */ #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_fc_port_deleted) - qla_target.tgt_fc_port_deleted(fcport->ha, fcport); + qla_target.tgt_fc_port_deleted(fcport->vha, fcport); #endif - spin_lock_irq(host->host_lock); - fcport->rport = NULL; + spin_lock_irqsave(host->host_lock, flags); + fcport->rport = fcport->drport = NULL; *((fc_port_t **)rport->dd_data) = NULL; - spin_unlock_irq(host->host_lock); + spin_unlock_irqrestore(host->host_lock, flags); + + if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags)) + return; + + if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) { + qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16); + return; + } } static void @@ -1827,56 +2138,74 @@ qla2x00_terminate_rport_io(struct fc_rport *rport) if (!fcport) return; - qla2x00_abort_fcport_cmds(fcport); -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) && !defined(CONFIG_SUSE_KERNEL) - scsi_target_unblock(&rport->dev); -#else + if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags)) + return; + + if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) { + qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16); + return; + } + /* - * It is not needed here, because the caller (fc_terminate_rport_io()) - * calls it. + * At this point all fcport's software-states are cleared. Perform any + * final cleanup of firmware resources (PCBs and XCBs). */ -#endif + if (fcport->loop_id != FC_NO_LOOP_ID && + !test_bit(UNLOADING, &fcport->vha->dpc_flags)) { + if (IS_FWI2_CAPABLE(fcport->vha->hw)) + fcport->vha->hw->isp_ops->fabric_logout(fcport->vha, + fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); + else + qla2x00_port_logout(fcport->vha, fcport); + } } static int qla2x00_issue_lip(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = shost_priv(shost); + scsi_qla_host_t *vha = shost_priv(shost); - qla2x00_loop_reset(ha); + qla2x00_loop_reset(vha); return 0; } static struct fc_host_statistics * qla2x00_get_fc_host_stats(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost)); + scsi_qla_host_t *vha = shost_priv(shost); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); int rval; struct link_statistics *stats; dma_addr_t stats_dma; struct fc_host_statistics *pfc_host_stat; - pfc_host_stat = &ha->fc_host_stat; + pfc_host_stat = &vha->fc_host_stat; memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics)); + if (test_bit(UNLOADING, &vha->dpc_flags)) + goto done; + + if (unlikely(pci_channel_offline(ha->pdev))) + goto done; + stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma); if (stats == NULL) { - DEBUG2_3_11(printk("%s(%ld): Failed to allocate memory.\n", - __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x707d, + "Failed to allocate memory for stats.\n"); goto done; } memset(stats, 0, DMA_POOL_SIZE); rval = QLA_FUNCTION_FAILED; if (IS_FWI2_CAPABLE(ha)) { - rval = qla24xx_get_isp_stats(ha, stats, stats_dma); - } else if (atomic_read(&ha->loop_state) == LOOP_READY && - !test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) && - !test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) && - !ha->dpc_active) { + rval = qla24xx_get_isp_stats(base_vha, stats, stats_dma); + } else if (atomic_read(&base_vha->loop_state) == LOOP_READY && + !qla2x00_reset_active(vha) && !ha->dpc_active) { /* Must be in a 'READY' state for statistics retrieval. */ - rval = qla2x00_get_link_status(ha, ha->loop_id, stats, - stats_dma); + rval = qla2x00_get_link_status(base_vha, base_vha->loop_id, + stats, stats_dma); } if (rval != QLA_SUCCESS) @@ -1889,11 +2218,14 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) pfc_host_stat->invalid_tx_word_count = stats->inval_xmit_word_cnt; pfc_host_stat->invalid_crc_count = stats->inval_crc_cnt; if (IS_FWI2_CAPABLE(ha)) { + pfc_host_stat->lip_count = stats->lip_cnt; pfc_host_stat->tx_frames = stats->tx_frames; pfc_host_stat->rx_frames = stats->rx_frames; pfc_host_stat->dumped_frames = stats->dumped_frames; pfc_host_stat->nos_count = stats->nos_rcvd; } + pfc_host_stat->fcp_input_megabytes = vha->qla_stats.input_bytes >> 20; + pfc_host_stat->fcp_output_megabytes = vha->qla_stats.output_bytes >> 20; done_free: dma_pool_free(ha->s_dma_pool, stats, stats_dma); @@ -1904,29 +2236,27 @@ done: static void qla2x00_get_host_symbolic_name(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = shost_priv(shost); + scsi_qla_host_t *vha = shost_priv(shost); - qla2x00_get_sym_node_name(ha, fc_host_symbolic_name(shost)); + qla2x00_get_sym_node_name(vha, fc_host_symbolic_name(shost)); } static void qla2x00_set_host_system_hostname(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = shost_priv(shost); + scsi_qla_host_t *vha = shost_priv(shost); - set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); + set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); } static void qla2x00_get_host_fabric_name(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = shost_priv(shost); - u64 node_name; + scsi_qla_host_t *vha = shost_priv(shost); + u64 node_name = 0xFFFFFFFF; - if (ha->device_flags & SWITCH_FOUND) - node_name = wwn_to_u64(ha->fabric_node_name); - else - node_name = wwn_to_u64(ha->node_name); + if (vha->device_flags & SWITCH_FOUND) + node_name = wwn_to_u64(vha->fabric_node_name); fc_host_fabric_name(shost) = node_name; } @@ -1934,34 +2264,58 @@ qla2x00_get_host_fabric_name(struct Scsi_Host *shost) static void qla2x00_get_host_port_state(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost)); + scsi_qla_host_t *vha = shost_priv(shost); + struct scsi_qla_host *base_vha = pci_get_drvdata(vha->hw->pdev); - if (!ha->flags.online) + if (!base_vha->flags.online) { fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; - else if (atomic_read(&ha->loop_state) == LOOP_TIMEOUT) - fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; - else + return; + } + + switch (atomic_read(&base_vha->loop_state)) { + case LOOP_UPDATE: + fc_host_port_state(shost) = FC_PORTSTATE_DIAGNOSTICS; + break; + case LOOP_DOWN: + if(test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags)) + fc_host_port_state(shost) = FC_PORTSTATE_DIAGNOSTICS; + else + fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; + break; + case LOOP_DEAD: + fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; + break; + case LOOP_READY: fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + break; + default: + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; + break; + } } static int qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) { int ret = 0; - scsi_qla_host_t *ha = shost_priv(fc_vport->shost); - scsi_qla_host_t *vha; + uint8_t qos = 0; + scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost); + scsi_qla_host_t *vha = NULL; + struct qla_hw_data *ha = base_vha->hw; + uint16_t options = 0; + int cnt; + struct req_que *req = ha->req_q_map[0]; ret = qla24xx_vport_create_req_sanity_check(fc_vport); if (ret) { - DEBUG15(printk("qla24xx_vport_create_req_sanity_check failed, " - "status %x\n", ret)); + ql_log(ql_log_warn, vha, 0x707e, + "Vport sanity check failed, status %x\n", ret); return (ret); } vha = qla24xx_create_vhost(fc_vport); if (vha == NULL) { - DEBUG15(printk ("qla24xx_create_vhost failed, vha = %p\n", - vha)); + ql_log(ql_log_warn, vha, 0x707f, "Vport create host failed.\n"); return FC_VPORT_FAILED; } if (disable) { @@ -1971,26 +2325,48 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) atomic_set(&vha->vp_state, VP_FAILED); /* ready to create vport */ - qla_printk(KERN_INFO, vha, "VP entry id %d assigned.\n", vha->vp_idx); + ql_log(ql_log_info, vha, 0x7080, + "VP entry id %d assigned.\n", vha->vp_idx); /* initialized vport states */ atomic_set(&vha->loop_state, LOOP_DOWN); - vha->vp_err_state= VP_ERR_PORTDWN; - vha->vp_prev_err_state= VP_ERR_UNKWN; + vha->vp_err_state = VP_ERR_PORTDWN; + vha->vp_prev_err_state = VP_ERR_UNKWN; /* Check if physical ha port is Up */ - if (atomic_read(&ha->loop_state) == LOOP_DOWN || - atomic_read(&ha->loop_state) == LOOP_DEAD) { + if (atomic_read(&base_vha->loop_state) == LOOP_DOWN || + atomic_read(&base_vha->loop_state) == LOOP_DEAD) { /* Don't retry or attempt login of this virtual port */ - DEBUG15(printk ("scsi(%ld): pport loop_state is not UP.\n", - vha->host_no)); + ql_dbg(ql_dbg_user, vha, 0x7081, + "Vport loop state is not UP.\n"); atomic_set(&vha->loop_state, LOOP_DEAD); if (!disable) fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); } - if (scsi_add_host(vha->host, &fc_vport->dev)) { - DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n", - vha->host_no, vha->vp_idx)); + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) { + if (ha->fw_attributes & BIT_4) { + int prot = 0; + vha->flags.difdix_supported = 1; + ql_dbg(ql_dbg_user, vha, 0x7082, + "Registered for DIF/DIX type 1 and 3 protection.\n"); + if (ql2xenabledif == 1) + prot = SHOST_DIX_TYPE0_PROTECTION; + scsi_host_set_prot(vha->host, + prot | SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE2_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION + | SHOST_DIX_TYPE1_PROTECTION + | SHOST_DIX_TYPE2_PROTECTION + | SHOST_DIX_TYPE3_PROTECTION); + scsi_host_set_guard(vha->host, SHOST_DIX_GUARD_CRC); + } else + vha->flags.difdix_supported = 0; + } + + if (scsi_add_host_with_dma(vha->host, &fc_vport->dev, + &ha->pdev->dev)) { + ql_dbg(ql_dbg_user, vha, 0x7083, + "scsi_add_host failure for VP[%d].\n", vha->vp_idx); goto vport_create_failed_2; } @@ -1998,9 +2374,9 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name); fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name); fc_host_supported_classes(vha->host) = - fc_host_supported_classes(ha->host); + fc_host_supported_classes(base_vha->host); fc_host_supported_speeds(vha->host) = - fc_host_supported_speeds(ha->host); + fc_host_supported_speeds(base_vha->host); #ifdef CONFIG_SCSI_QLA2XXX_TARGET vha->tgt = NULL; @@ -2009,27 +2385,67 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) mutex_init(&vha->tgt_host_action_mutex); qla_clear_tgt_mode(vha); qla2x00_send_enable_lun(vha, false); - if (IS_QLA24XX_TYPE(vha)) - vha->atio_q_length = ATIO_ENTRY_CNT_24XX; - else if (IS_QLA25XX(vha)) - vha->atio_q_length = ATIO_ENTRY_CNT_24XX; + if (IS_QLA24XX_TYPE(ha)) + vha->hw->atio_q_length = ATIO_ENTRY_CNT_24XX; + else if (IS_QLA25XX(ha)) + vha->hw->atio_q_length = ATIO_ENTRY_CNT_24XX; - if (qla_target.tgt_host_action != NULL) - qla_target.tgt_host_action(vha, ADD_TARGET); + if (qla_target.tgt_host_action != NULL) { + if (qla_target.tgt_host_action(vha, ADD_TARGET) != 0) + goto vport_create_failed_2; + } /* * Must be after tgt_host_action() to not race with * qla2xxx_add_targets(). */ #endif - qla24xx_vport_disable(fc_vport, disable); + qla24xx_init_vp(vha); + if (ha->flags.cpu_affinity_enabled) { + req = ha->req_q_map[1]; + ql_dbg(ql_dbg_multiq, vha, 0xc000, + "Request queue %p attached with " + "VP[%d], cpu affinity =%d\n", + req, vha->vp_idx, ha->flags.cpu_affinity_enabled); + goto vport_queue; + } else if (ql2xmaxqueues == 1 || !ha->npiv_info) + goto vport_queue; + /* Create a request queue in QoS mode for the vport */ + for (cnt = 0; cnt < ha->nvram_npiv_size; cnt++) { + if (memcmp(ha->npiv_info[cnt].port_name, vha->port_name, 8) == 0 + && memcmp(ha->npiv_info[cnt].node_name, vha->node_name, + 8) == 0) { + qos = ha->npiv_info[cnt].q_qos; + break; + } + } + + if (qos) { + ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0, + qos); + if (!ret) + ql_log(ql_log_warn, vha, 0x7084, + "Can't create request queue for VP[%d]\n", + vha->vp_idx); + else { + ql_dbg(ql_dbg_multiq, vha, 0xc001, + "Request Que:%d Q0s: %d) created for VP[%d]\n", + ret, qos, vha->vp_idx); + ql_dbg(ql_dbg_user, vha, 0x7085, + "Request Que:%d Q0s: %d) created for VP[%d]\n", + ret, qos, vha->vp_idx); + req = ha->req_q_map[ret]; + } + } + +vport_queue: + vha->req = req; return 0; + vport_create_failed_2: qla24xx_disable_vp(vha); qla24xx_deallocate_vp_id(vha); - kfree(vha->port_name); - kfree(vha->node_name); scsi_host_put(vha->host); return FC_VPORT_FAILED; } @@ -2037,38 +2453,57 @@ vport_create_failed_2: static int qla24xx_vport_delete(struct fc_vport *fc_vport) { - scsi_qla_host_t *ha = shost_priv(fc_vport->shost); scsi_qla_host_t *vha = fc_vport->dd_data; + struct qla_hw_data *ha = vha->hw; + uint16_t id = vha->vp_idx; + + while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) || + test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) + msleep(1000); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_host_action != NULL) qla_target.tgt_host_action(vha, REMOVE_TARGET); #endif - qla24xx_disable_vp(vha); + + vha->flags.delete_progress = 1; + + fc_remove_host(vha->host); + + scsi_remove_host(vha->host); + + /* Allow timer to run to drain queued items, when removing vp */ qla24xx_deallocate_vp_id(vha); + if (vha->timer_active) { + qla2x00_vp_stop_timer(vha); + ql_dbg(ql_dbg_user, vha, 0x7086, + "Timer for the VP[%d] has stopped\n", vha->vp_idx); + } + + /* No pending activities shall be there on the vha now */ + if (ql2xextended_error_logging & ql_dbg_user) + msleep(20); /* Just to see if something falls on + * the net we have placed below */ + + BUG_ON(atomic_read(&vha->vref_count)); + + qla2x00_free_fcports(vha); + mutex_lock(&ha->vport_lock); ha->cur_vport_count--; clear_bit(vha->vp_idx, ha->vp_idx_map); mutex_unlock(&ha->vport_lock); - kfree(vha->node_name); - kfree(vha->port_name); - - if (vha->timer_active) { - qla2x00_vp_stop_timer(vha); - DEBUG15(printk ("scsi(%ld): timer for the vport[%d] = %p " - "has stopped\n", - vha->host_no, vha->vp_idx, vha)); - } - - fc_remove_host(vha->host); - - scsi_remove_host(vha->host); + if (vha->req->id && !ha->flags.cpu_affinity_enabled) { + if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0x7087, + "Queue delete failed.\n"); + } + ql_log(ql_log_info, vha, 0x7088, "VP[%d] deleted.\n", id); scsi_host_put(vha->host); - return 0; } @@ -2128,6 +2563,8 @@ struct fc_function_template qla2xxx_transport_functions = { .vport_create = qla24xx_vport_create, .vport_disable = qla24xx_vport_disable, .vport_delete = qla24xx_vport_delete, + .bsg_request = qla24xx_bsg_request, + .bsg_timeout = qla24xx_bsg_timeout, }; struct fc_function_template qla2xxx_transport_vport_functions = { @@ -2168,21 +2605,29 @@ struct fc_function_template qla2xxx_transport_vport_functions = { .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk, .terminate_rport_io = qla2x00_terminate_rport_io, .get_fc_host_stats = qla2x00_get_fc_host_stats, + .bsg_request = qla24xx_bsg_request, + .bsg_timeout = qla24xx_bsg_timeout, }; void -qla2x00_init_host_attr(scsi_qla_host_t *ha) +qla2x00_init_host_attr(scsi_qla_host_t *vha) { + struct qla_hw_data *ha = vha->hw; u32 speed = FC_PORTSPEED_UNKNOWN; - fc_host_node_name(ha->host) = wwn_to_u64(ha->node_name); - fc_host_port_name(ha->host) = wwn_to_u64(ha->port_name); - fc_host_supported_classes(ha->host) = ha->enable_class_2 ? + fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name); + fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name); + fc_host_supported_classes(vha->host) = ha->enable_class_2 ? (FC_COS_CLASS2|FC_COS_CLASS3) : FC_COS_CLASS3; - fc_host_max_npiv_vports(ha->host) = ha->max_npiv_vports;; - fc_host_npiv_vports_inuse(ha->host) = ha->cur_vport_count; + fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports; + fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count; - if (IS_QLA25XX(ha)) + if (IS_CNA_CAPABLE(ha)) + speed = FC_PORTSPEED_10GBIT; + else if (IS_QLA2031(ha)) + speed = FC_PORTSPEED_16GBIT | FC_PORTSPEED_8GBIT | + FC_PORTSPEED_4GBIT; + else if (IS_QLA25XX(ha)) speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT; else if (IS_QLA24XX_TYPE(ha)) @@ -2192,5 +2637,5 @@ qla2x00_init_host_attr(scsi_qla_host_t *ha) speed = FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT; else speed = FC_PORTSPEED_1GBIT; - fc_host_supported_speeds(ha->host) = speed; + fc_host_supported_speeds(vha->host) = speed; } diff --git a/qla2x00t/qla_bsg.c b/qla2x00t/qla_bsg.c new file mode 100644 index 000000000..1b5922dfc --- /dev/null +++ b/qla2x00t/qla_bsg.c @@ -0,0 +1,1711 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2011 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#include "qla_def.h" + +#include +#include +#include + +/* BSG support for ELS/CT pass through */ +void +qla2x00_bsg_job_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t*)ptr; + struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; + + bsg_job->reply->result = res; + bsg_job->job_done(bsg_job); + sp->free(vha, sp); +} + +void +qla2x00_bsg_sp_free(void *data, void *ptr) +{ + srb_t *sp = (srb_t*)ptr; + struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; + struct qla_hw_data *ha = vha->hw; + + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + + if (sp->type == SRB_CT_CMD || + sp->type == SRB_ELS_CMD_HST) + kfree(sp->fcport); + mempool_free(sp, vha->hw->srb_mempool); +} + +int +qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *vha, + struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag) +{ + int i, ret, num_valid; + uint8_t *bcode; + struct qla_fcp_prio_entry *pri_entry; + uint32_t *bcode_val_ptr, bcode_val; + + ret = 1; + num_valid = 0; + bcode = (uint8_t *)pri_cfg; + bcode_val_ptr = (uint32_t *)pri_cfg; + bcode_val = (uint32_t)(*bcode_val_ptr); + + if (bcode_val == 0xFFFFFFFF) { + /* No FCP Priority config data in flash */ + ql_dbg(ql_dbg_user, vha, 0x7051, + "No FCP Priority config data.\n"); + return 0; + } + + if (bcode[0] != 'H' || bcode[1] != 'Q' || bcode[2] != 'O' || + bcode[3] != 'S') { + /* Invalid FCP priority data header*/ + ql_dbg(ql_dbg_user, vha, 0x7052, + "Invalid FCP Priority data header. bcode=0x%x.\n", + bcode_val); + return 0; + } + if (flag != 1) + return ret; + + pri_entry = &pri_cfg->entry[0]; + for (i = 0; i < pri_cfg->num_entries; i++) { + if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID) + num_valid++; + pri_entry++; + } + + if (num_valid == 0) { + /* No valid FCP priority data entries */ + ql_dbg(ql_dbg_user, vha, 0x7053, + "No valid FCP Priority data entries.\n"); + ret = 0; + } else { + /* FCP priority data is valid */ + ql_dbg(ql_dbg_user, vha, 0x7054, + "Valid FCP priority data. num entries = %d.\n", + num_valid); + } + + return ret; +} + +static int +qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int ret = 0; + uint32_t len; + uint32_t oper; + + if (!(IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA82XX(ha))) { + ret = -EINVAL; + goto exit_fcp_prio_cfg; + } + + /* Get the sub command */ + oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + /* Only set config is allowed if config memory is not allocated */ + if (!ha->fcp_prio_cfg && (oper != QLFC_FCP_PRIO_SET_CONFIG)) { + ret = -EINVAL; + goto exit_fcp_prio_cfg; + } + switch (oper) { + case QLFC_FCP_PRIO_DISABLE: + if (ha->flags.fcp_prio_enabled) { + ha->flags.fcp_prio_enabled = 0; + ha->fcp_prio_cfg->attributes &= + ~FCP_PRIO_ATTR_ENABLE; + qla24xx_update_all_fcp_prio(vha); + bsg_job->reply->result = DID_OK; + } else { + ret = -EINVAL; + bsg_job->reply->result = (DID_ERROR << 16); + goto exit_fcp_prio_cfg; + } + break; + + case QLFC_FCP_PRIO_ENABLE: + if (!ha->flags.fcp_prio_enabled) { + if (ha->fcp_prio_cfg) { + ha->flags.fcp_prio_enabled = 1; + ha->fcp_prio_cfg->attributes |= + FCP_PRIO_ATTR_ENABLE; + qla24xx_update_all_fcp_prio(vha); + bsg_job->reply->result = DID_OK; + } else { + ret = -EINVAL; + bsg_job->reply->result = (DID_ERROR << 16); + goto exit_fcp_prio_cfg; + } + } + break; + + case QLFC_FCP_PRIO_GET_CONFIG: + len = bsg_job->reply_payload.payload_len; + if (!len || len > FCP_PRIO_CFG_SIZE) { + ret = -EINVAL; + bsg_job->reply->result = (DID_ERROR << 16); + goto exit_fcp_prio_cfg; + } + + bsg_job->reply->result = DID_OK; + bsg_job->reply->reply_payload_rcv_len = + sg_copy_from_buffer( + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, ha->fcp_prio_cfg, + len); + + break; + + case QLFC_FCP_PRIO_SET_CONFIG: + len = bsg_job->request_payload.payload_len; + if (!len || len > FCP_PRIO_CFG_SIZE) { + bsg_job->reply->result = (DID_ERROR << 16); + ret = -EINVAL; + goto exit_fcp_prio_cfg; + } + + if (!ha->fcp_prio_cfg) { + ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); + if (!ha->fcp_prio_cfg) { + ql_log(ql_log_warn, vha, 0x7050, + "Unable to allocate memory for fcp prio " + "config data (%x).\n", FCP_PRIO_CFG_SIZE); + bsg_job->reply->result = (DID_ERROR << 16); + ret = -ENOMEM; + goto exit_fcp_prio_cfg; + } + } + + memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE); + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, ha->fcp_prio_cfg, + FCP_PRIO_CFG_SIZE); + + /* validate fcp priority data */ + + if (!qla24xx_fcp_prio_cfg_valid(vha, + (struct qla_fcp_prio_cfg *) ha->fcp_prio_cfg, 1)) { + bsg_job->reply->result = (DID_ERROR << 16); + ret = -EINVAL; + /* If buffer was invalidatic int + * fcp_prio_cfg is of no use + */ + vfree(ha->fcp_prio_cfg); + ha->fcp_prio_cfg = NULL; + goto exit_fcp_prio_cfg; + } + + ha->flags.fcp_prio_enabled = 0; + if (ha->fcp_prio_cfg->attributes & FCP_PRIO_ATTR_ENABLE) + ha->flags.fcp_prio_enabled = 1; + qla24xx_update_all_fcp_prio(vha); + bsg_job->reply->result = DID_OK; + break; + default: + ret = -EINVAL; + break; + } +exit_fcp_prio_cfg: + bsg_job->job_done(bsg_job); + return ret; +} + +static int +qla2x00_process_els(struct fc_bsg_job *bsg_job) +{ + struct fc_rport *rport; + fc_port_t *fcport = NULL; + struct Scsi_Host *host; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + srb_t *sp; + const char *type; + int req_sg_cnt, rsp_sg_cnt; + int rval = (DRIVER_ERROR << 16); + uint16_t nextlid = 0; + +#ifdef __COVERITY__ + BUG_ON(bsg_job->request->msgcode != FC_BSG_RPT_ELS && + bsg_job->request->msgcode != FC_BSG_HST_ELS_NOLOGIN); +#endif + + if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { + rport = bsg_job->rport; + fcport = *(fc_port_t **) rport->dd_data; + host = rport_to_shost(rport); + vha = shost_priv(host); + ha = vha->hw; + type = "FC_BSG_RPT_ELS"; + } else { + host = bsg_job->shost; + vha = shost_priv(host); + ha = vha->hw; + type = "FC_BSG_HST_ELS_NOLOGIN"; + } + + /* pass through is supported only for ISP 4Gb or higher */ + if (!IS_FWI2_CAPABLE(ha)) { + ql_dbg(ql_dbg_user, vha, 0x7001, + "ELS passthru not supported for ISP23xx based adapters.\n"); + rval = -EPERM; + goto done; + } + + /* Multiple SG's are not supported for ELS requests */ + if (bsg_job->request_payload.sg_cnt > 1 || + bsg_job->reply_payload.sg_cnt > 1) { + ql_dbg(ql_dbg_user, vha, 0x7002, + "Multiple SG's are not suppored for ELS requests, " + "request_sg_cnt=%x reply_sg_cnt=%x.\n", + bsg_job->request_payload.sg_cnt, + bsg_job->reply_payload.sg_cnt); + rval = -EPERM; + goto done; + } + + /* ELS request for rport */ + if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { + /* make sure the rport is logged in, + * if not perform fabric login + */ + if (qla2x00_fabric_login(vha, fcport, &nextlid)) { + ql_dbg(ql_dbg_user, vha, 0x7003, + "Failed to login port %06X for ELS passthru.\n", + fcport->d_id.b24); + rval = -EIO; + goto done; + } + } else { + /* Allocate a dummy fcport structure, since functions + * preparing the IOCB and mailbox command retrieves port + * specific information from fcport structure. For Host based + * ELS commands there will be no fcport structure allocated + */ + fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (!fcport) { + rval = -ENOMEM; + goto done; + } + + /* Initialize all required fields of fcport */ + fcport->vha = vha; + fcport->d_id.b.al_pa = + bsg_job->request->rqst_data.h_els.port_id[0]; + fcport->d_id.b.area = + bsg_job->request->rqst_data.h_els.port_id[1]; + fcport->d_id.b.domain = + bsg_job->request->rqst_data.h_els.port_id[2]; + fcport->loop_id = + (fcport->d_id.b.al_pa == 0xFD) ? + NPH_FABRIC_CONTROLLER : NPH_F_PORT; + } + + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n"); + rval = -EIO; + goto done_free_fcport; + } + + req_sg_cnt = + dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + if (!req_sg_cnt) { + rval = -ENOMEM; + goto done_free_fcport; + } + + rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (!rsp_sg_cnt) { + rval = -ENOMEM; + goto done_free_fcport; + } + + if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + ql_log(ql_log_warn, vha, 0x7008, + "dma mapping resulted in different sg counts, " + "request_sg_cnt: %x dma_request_sg_cnt:%x reply_sg_cnt:%x " + "dma_reply_sg_cnt:%x.\n", bsg_job->request_payload.sg_cnt, + req_sg_cnt, bsg_job->reply_payload.sg_cnt, rsp_sg_cnt); + rval = -EAGAIN; + goto done_unmap_sg; + } + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) { + rval = -ENOMEM; + goto done_unmap_sg; + } + + sp->type = + (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? + SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST); + sp->name = + (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? + "bsg_els_rpt" : "bsg_els_hst"); + sp->u.bsg_job = bsg_job; + sp->free = qla2x00_bsg_sp_free; + sp->done = qla2x00_bsg_job_done; + + ql_dbg(ql_dbg_user, vha, 0x700a, + "bsg rqst type: %s els type: %x - loop-id=%x " + "portid=%-2x%02x%02x.\n", type, + bsg_job->request->rqst_data.h_els.command_code, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x700e, + "qla2x00_start_sp failed = %d\n", rval); + mempool_free(sp, ha->srb_mempool); + rval = -EIO; + goto done_unmap_sg; + } + return rval; + +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + goto done_free_fcport; + +done_free_fcport: + if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN) + kfree(fcport); +done: + return rval; +} + +inline uint16_t +qla24xx_calc_ct_iocbs(uint16_t dsds) +{ + uint16_t iocbs; + + iocbs = 1; + if (dsds > 2) { + iocbs += (dsds - 2) / 5; + if ((dsds - 2) % 5) + iocbs++; + } + return iocbs; +} + +static int +qla2x00_process_ct(struct fc_bsg_job *bsg_job) +{ + srb_t *sp; + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = (DRIVER_ERROR << 16); + int req_sg_cnt, rsp_sg_cnt; + uint16_t loop_id; + struct fc_port *fcport; + char *type = "FC_BSG_HST_CT"; + + req_sg_cnt = + dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + if (!req_sg_cnt) { + ql_log(ql_log_warn, vha, 0x700f, + "dma_map_sg return %d for request\n", req_sg_cnt); + rval = -ENOMEM; + goto done; + } + + rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (!rsp_sg_cnt) { + ql_log(ql_log_warn, vha, 0x7010, + "dma_map_sg return %d for reply\n", rsp_sg_cnt); + rval = -ENOMEM; + goto done; + } + + if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + ql_log(ql_log_warn, vha, 0x7011, + "request_sg_cnt: %x dma_request_sg_cnt: %x reply_sg_cnt:%x " + "dma_reply_sg_cnt: %x\n", bsg_job->request_payload.sg_cnt, + req_sg_cnt, bsg_job->reply_payload.sg_cnt, rsp_sg_cnt); + rval = -EAGAIN; + goto done_unmap_sg; + } + + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x7012, + "Host is not online.\n"); + rval = -EIO; + goto done_unmap_sg; + } + + loop_id = + (bsg_job->request->rqst_data.h_ct.preamble_word1 & 0xFF000000) + >> 24; + switch (loop_id) { + case 0xFC: + loop_id = cpu_to_le16(NPH_SNS); + break; + case 0xFA: + loop_id = vha->mgmt_svr_loop_id; + break; + default: + ql_dbg(ql_dbg_user, vha, 0x7013, + "Unknown loop id: %x.\n", loop_id); + rval = -EINVAL; + goto done_unmap_sg; + } + + /* Allocate a dummy fcport structure, since functions preparing the + * IOCB and mailbox command retrieves port specific information + * from fcport structure. For Host based ELS commands there will be + * no fcport structure allocated + */ + fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (!fcport) { + ql_log(ql_log_warn, vha, 0x7014, + "Failed to allocate fcport.\n"); + rval = -ENOMEM; + goto done_unmap_sg; + } + + /* Initialize all required fields of fcport */ + fcport->vha = vha; + fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0]; + fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1]; + fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2]; + fcport->loop_id = loop_id; + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) { + ql_log(ql_log_warn, vha, 0x7015, + "qla2x00_get_sp failed.\n"); + rval = -ENOMEM; + goto done_free_fcport; + } + + sp->type = SRB_CT_CMD; + sp->name = "bsg_ct"; + sp->iocbs = qla24xx_calc_ct_iocbs(req_sg_cnt + rsp_sg_cnt); + sp->u.bsg_job = bsg_job; + sp->free = qla2x00_bsg_sp_free; + sp->done = qla2x00_bsg_job_done; + + ql_dbg(ql_dbg_user, vha, 0x7016, + "bsg rqst type: %s else type: %x - " + "loop-id=%x portid=%02x%02x%02x.\n", type, + (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16), + fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa); + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7017, + "qla2x00_start_sp failed=%d.\n", rval); + mempool_free(sp, ha->srb_mempool); + rval = -EIO; + goto done_free_fcport; + } + return rval; + +done_free_fcport: + kfree(fcport); +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); +done: + return rval; +} + +/* Set the port configuration to enable the + * internal loopback on ISP81XX + */ +static inline int +qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, + uint16_t *new_config) +{ + int ret = 0; + int rval = 0; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + goto done_set_internal; + + new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); + memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; + + ha->notify_dcbx_comp = 1; + ret = qla81xx_set_port_config(vha, new_config); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7021, + "set port config failed.\n"); + ha->notify_dcbx_comp = 0; + rval = -EINVAL; + goto done_set_internal; + } + + /* Wait for DCBX complete event */ + if (!wait_for_completion_timeout(&ha->dcbx_comp, (20 * HZ))) { + ql_dbg(ql_dbg_user, vha, 0x7022, + "State change notification not received.\n"); + } else + ql_dbg(ql_dbg_user, vha, 0x7023, + "State change received.\n"); + + ha->notify_dcbx_comp = 0; + +done_set_internal: + return rval; +} + +/* Set the port configuration to disable the + * internal loopback on ISP81XX + */ +static inline int +qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, + int wait) +{ + int ret = 0; + int rval = 0; + uint16_t new_config[4]; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + goto done_reset_internal; + + memset(new_config, 0 , sizeof(new_config)); + if ((config[0] & INTERNAL_LOOPBACK_MASK) >> 1 == + ENABLE_INTERNAL_LOOPBACK) { + new_config[0] = config[0] & ~INTERNAL_LOOPBACK_MASK; + memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; + + ha->notify_dcbx_comp = wait; + ret = qla81xx_set_port_config(vha, new_config); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7025, + "Set port config failed.\n"); + ha->notify_dcbx_comp = 0; + rval = -EINVAL; + goto done_reset_internal; + } + + /* Wait for DCBX complete event */ + if (wait && !wait_for_completion_timeout(&ha->dcbx_comp, + (20 * HZ))) { + ql_dbg(ql_dbg_user, vha, 0x7026, + "State change notification not received.\n"); + ha->notify_dcbx_comp = 0; + rval = -EINVAL; + goto done_reset_internal; + } else + ql_dbg(ql_dbg_user, vha, 0x7027, + "State change received.\n"); + + ha->notify_dcbx_comp = 0; + } +done_reset_internal: + return rval; +} + +static int +qla2x00_process_loopback(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval; + uint8_t command_sent; + char *type; + struct msg_echo_lb elreq; + uint16_t response[MAILBOX_REGISTER_COUNT]; + uint16_t config[4], new_config[4]; + uint8_t *fw_sts_ptr; + uint8_t *req_data = NULL; + dma_addr_t req_data_dma; + uint32_t req_data_len; + uint8_t *rsp_data = NULL; + dma_addr_t rsp_data_dma; + uint32_t rsp_data_len; + + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x7019, "Host is not online.\n"); + return -EIO; + } + + elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (!elreq.req_sg_cnt) { + ql_log(ql_log_warn, vha, 0x701a, + "dma_map_sg returned %d for request.\n", elreq.req_sg_cnt); + return -ENOMEM; + } + + elreq.rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, + DMA_FROM_DEVICE); + + if (!elreq.rsp_sg_cnt) { + ql_log(ql_log_warn, vha, 0x701b, + "dma_map_sg returned %d for reply.\n", elreq.rsp_sg_cnt); + rval = -ENOMEM; + goto done_unmap_req_sg; + } + + if ((elreq.req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + ql_log(ql_log_warn, vha, 0x701c, + "dma mapping resulted in different sg counts, " + "request_sg_cnt: %x dma_request_sg_cnt: %x " + "reply_sg_cnt: %x dma_reply_sg_cnt: %x.\n", + bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt, + bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt); + + rval = -EAGAIN; + goto done_unmap_sg; + } + req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; + req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len, + &req_data_dma, GFP_KERNEL); + if (!req_data) { + ql_log(ql_log_warn, vha, 0x701d, + "dma alloc failed for req_data.\n"); + rval = -ENOMEM; + goto done_unmap_sg; + } + + rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len, + &rsp_data_dma, GFP_KERNEL); + if (!rsp_data) { + ql_log(ql_log_warn, vha, 0x7004, + "dma alloc failed for rsp_data.\n"); + rval = -ENOMEM; + goto done_free_dma_req; + } + + /* Copy the request buffer in req_data now */ + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, req_data, req_data_len); + + elreq.send_dma = req_data_dma; + elreq.rcv_dma = rsp_data_dma; + elreq.transfer_size = req_data_len; + + elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + if ((ha->current_topology == ISP_CFG_F || + ((IS_QLA81XX(ha) || IS_QLA8031(ha)) && + le32_to_cpu(*(uint32_t *)req_data) == ELS_OPCODE_BYTE + && req_data_len == MAX_ELS_FRAME_PAYLOAD)) && + elreq.options == EXTERNAL_LOOPBACK) { + type = "FC_BSG_HST_VENDOR_ECHO_DIAG"; + ql_dbg(ql_dbg_user, vha, 0x701e, + "BSG request type: %s.\n", type); + command_sent = INT_DEF_LB_ECHO_CMD; + rval = qla2x00_echo_test(vha, &elreq, response); + } else { + if (IS_QLA81XX(ha) || IS_QLA8031(ha)) { + memset(config, 0, sizeof(config)); + memset(new_config, 0, sizeof(new_config)); + if (qla81xx_get_port_config(vha, config)) { + ql_log(ql_log_warn, vha, 0x701f, + "Get port config failed.\n"); + bsg_job->reply->result = (DID_ERROR << 16); + rval = -EPERM; + goto done_free_dma_req; + } + + if (elreq.options != EXTERNAL_LOOPBACK) { + ql_dbg(ql_dbg_user, vha, 0x7020, + "Internal: current port config = %x\n", + config[0]); + if (qla81xx_set_internal_loopback(vha, config, + new_config)) { + ql_log(ql_log_warn, vha, 0x7024, + "Internal loopback failed.\n"); + bsg_job->reply->result = + (DID_ERROR << 16); + rval = -EPERM; + goto done_free_dma_req; + } + } else { + /* For external loopback to work + * ensure internal loopback is disabled + */ + if (qla81xx_reset_internal_loopback(vha, + config, 1)) { + bsg_job->reply->result = + (DID_ERROR << 16); + rval = -EPERM; + goto done_free_dma_req; + } + } + + type = "FC_BSG_HST_VENDOR_LOOPBACK"; + ql_dbg(ql_dbg_user, vha, 0x7028, + "BSG request type: %s.\n", type); + + command_sent = INT_DEF_LB_LOOPBACK_CMD; + rval = qla2x00_loopback_test(vha, &elreq, response); + + if (new_config[0]) { + /* Revert back to original port config + * Also clear internal loopback + */ + qla81xx_reset_internal_loopback(vha, + new_config, 0); + } + + if (response[0] == MBS_COMMAND_ERROR && + response[1] == MBS_LB_RESET) { + ql_log(ql_log_warn, vha, 0x7029, + "MBX command error, Aborting ISP.\n"); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_chip_reset(vha); + /* Also reset the MPI */ + if (qla81xx_restart_mpi_firmware(vha) != + QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x702a, + "MPI reset failed.\n"); + } + + bsg_job->reply->result = (DID_ERROR << 16); + rval = -EIO; + goto done_free_dma_req; + } + } else { + type = "FC_BSG_HST_VENDOR_LOOPBACK"; + ql_dbg(ql_dbg_user, vha, 0x702b, + "BSG request type: %s.\n", type); + command_sent = INT_DEF_LB_LOOPBACK_CMD; + rval = qla2x00_loopback_test(vha, &elreq, response); + } + } + + if (rval) { + ql_log(ql_log_warn, vha, 0x702c, + "Vendor request %s failed.\n", type); + + fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + + sizeof(struct fc_bsg_reply); + + memcpy(fw_sts_ptr, response, sizeof(response)); + fw_sts_ptr += sizeof(response); + *fw_sts_ptr = command_sent; + rval = 0; + bsg_job->reply->result = (DID_ERROR << 16); + } else { + ql_dbg(ql_dbg_user, vha, 0x702d, + "Vendor request %s completed.\n", type); + + bsg_job->reply_len = sizeof(struct fc_bsg_reply) + + sizeof(response) + sizeof(uint8_t); + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + + sizeof(struct fc_bsg_reply); + memcpy(fw_sts_ptr, response, sizeof(response)); + fw_sts_ptr += sizeof(response); + *fw_sts_ptr = command_sent; + bsg_job->reply->result = DID_OK; + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, rsp_data, + rsp_data_len); + } + bsg_job->job_done(bsg_job); + + dma_free_coherent(&ha->pdev->dev, rsp_data_len, + rsp_data, rsp_data_dma); +done_free_dma_req: + dma_free_coherent(&ha->pdev->dev, req_data_len, + req_data, req_data_dma); +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); +done_unmap_req_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + return rval; +} + +static int +qla84xx_reset(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + uint32_t flag; + + if (!IS_QLA84XX(ha)) { + ql_dbg(ql_dbg_user, vha, 0x702f, "Not 84xx, exiting.\n"); + return -EINVAL; + } + + flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW); + + if (rval) { + ql_log(ql_log_warn, vha, 0x7030, + "Vendor request 84xx reset failed.\n"); + rval = 0; + bsg_job->reply->result = (DID_ERROR << 16); + + } else { + ql_dbg(ql_dbg_user, vha, 0x7031, + "Vendor request 84xx reset completed.\n"); + bsg_job->reply->result = DID_OK; + } + + bsg_job->job_done(bsg_job); + return rval; +} + +static int +qla84xx_updatefw(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct verify_chip_entry_84xx *mn = NULL; + dma_addr_t mn_dma, fw_dma; + void *fw_buf = NULL; + int rval = 0; + uint32_t sg_cnt; + uint32_t data_len; + uint16_t options; + uint32_t flag; + uint32_t fw_ver; + + if (!IS_QLA84XX(ha)) { + ql_dbg(ql_dbg_user, vha, 0x7032, + "Not 84xx, exiting.\n"); + return -EINVAL; + } + + sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + if (!sg_cnt) { + ql_log(ql_log_warn, vha, 0x7033, + "dma_map_sg returned %d for request.\n", sg_cnt); + return -ENOMEM; + } + + if (sg_cnt != bsg_job->request_payload.sg_cnt) { + ql_log(ql_log_warn, vha, 0x7034, + "DMA mapping resulted in different sg counts, " + "request_sg_cnt: %x dma_request_sg_cnt: %x.\n", + bsg_job->request_payload.sg_cnt, sg_cnt); + rval = -EAGAIN; + goto done_unmap_sg; + } + + data_len = bsg_job->request_payload.payload_len; + fw_buf = dma_alloc_coherent(&ha->pdev->dev, data_len, + &fw_dma, GFP_KERNEL); + if (!fw_buf) { + ql_log(ql_log_warn, vha, 0x7035, + "DMA alloc failed for fw_buf.\n"); + rval = -ENOMEM; + goto done_unmap_sg; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, fw_buf, data_len); + + mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); + if (!mn) { + ql_log(ql_log_warn, vha, 0x7036, + "DMA alloc failed for fw buffer.\n"); + rval = -ENOMEM; + goto done_free_fw_buf; + } + + flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + fw_ver = le32_to_cpu(*((uint32_t *)((uint32_t *)fw_buf + 2))); + + memset(mn, 0, sizeof(struct access_chip_84xx)); + mn->entry_type = VERIFY_CHIP_IOCB_TYPE; + mn->entry_count = 1; + + options = VCO_FORCE_UPDATE | VCO_END_OF_DATA; + if (flag == A84_ISSUE_UPDATE_DIAGFW_CMD) + options |= VCO_DIAG_FW; + + mn->options = cpu_to_le16(options); + mn->fw_ver = cpu_to_le32(fw_ver); + mn->fw_size = cpu_to_le32(data_len); + mn->fw_seq_size = cpu_to_le32(data_len); + mn->dseg_address[0] = cpu_to_le32(LSD(fw_dma)); + mn->dseg_address[1] = cpu_to_le32(MSD(fw_dma)); + mn->dseg_length = cpu_to_le32(data_len); + mn->data_seg_cnt = cpu_to_le16(1); + + rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120); + + if (rval) { + ql_log(ql_log_warn, vha, 0x7037, + "Vendor request 84xx updatefw failed.\n"); + + rval = 0; + bsg_job->reply->result = (DID_ERROR << 16); + } else { + ql_dbg(ql_dbg_user, vha, 0x7038, + "Vendor request 84xx updatefw completed.\n"); + + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->result = DID_OK; + } + + bsg_job->job_done(bsg_job); + dma_pool_free(ha->s_dma_pool, mn, mn_dma); + +done_free_fw_buf: + dma_free_coherent(&ha->pdev->dev, data_len, fw_buf, fw_dma); + +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + return rval; +} + +static int +qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct access_chip_84xx *mn = NULL; + dma_addr_t mn_dma, mgmt_dma; + void *mgmt_b = NULL; + int rval = 0; + struct qla_bsg_a84_mgmt *ql84_mgmt; + uint32_t sg_cnt; + uint32_t data_len = 0; + uint32_t dma_direction = DMA_NONE; + + if (!IS_QLA84XX(ha)) { + ql_log(ql_log_warn, vha, 0x703a, + "Not 84xx, exiting.\n"); + return -EINVAL; + } + + ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request + + sizeof(struct fc_bsg_request)); + + mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); + if (!mn) { + ql_log(ql_log_warn, vha, 0x703c, + "DMA alloc failed for fw buffer.\n"); + return -ENOMEM; + } + + memset(mn, 0, sizeof(struct access_chip_84xx)); + mn->entry_type = ACCESS_CHIP_IOCB_TYPE; + mn->entry_count = 1; + + switch (ql84_mgmt->mgmt.cmd) { + case QLA84_MGMT_READ_MEM: + case QLA84_MGMT_GET_INFO: + sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (!sg_cnt) { + ql_log(ql_log_warn, vha, 0x703d, + "dma_map_sg returned %d for reply.\n", sg_cnt); + rval = -ENOMEM; + goto exit_mgmt; + } + + dma_direction = DMA_FROM_DEVICE; + + if (sg_cnt != bsg_job->reply_payload.sg_cnt) { + ql_log(ql_log_warn, vha, 0x703e, + "DMA mapping resulted in different sg counts, " + "reply_sg_cnt: %x dma_reply_sg_cnt: %x.\n", + bsg_job->reply_payload.sg_cnt, sg_cnt); + rval = -EAGAIN; + goto done_unmap_sg; + } + + data_len = bsg_job->reply_payload.payload_len; + + mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, + &mgmt_dma, GFP_KERNEL); + if (!mgmt_b) { + ql_log(ql_log_warn, vha, 0x703f, + "DMA alloc failed for mgmt_b.\n"); + rval = -ENOMEM; + goto done_unmap_sg; + } + + if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) { + mn->options = cpu_to_le16(ACO_DUMP_MEMORY); + mn->parameter1 = + cpu_to_le32( + ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); + + } else if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO) { + mn->options = cpu_to_le16(ACO_REQUEST_INFO); + mn->parameter1 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.info.type); + + mn->parameter2 = + cpu_to_le32( + ql84_mgmt->mgmt.mgmtp.u.info.context); + } + break; + + case QLA84_MGMT_WRITE_MEM: + sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (!sg_cnt) { + ql_log(ql_log_warn, vha, 0x7040, + "dma_map_sg returned %d.\n", sg_cnt); + rval = -ENOMEM; + goto exit_mgmt; + } + + dma_direction = DMA_TO_DEVICE; + + if (sg_cnt != bsg_job->request_payload.sg_cnt) { + ql_log(ql_log_warn, vha, 0x7041, + "DMA mapping resulted in different sg counts, " + "request_sg_cnt: %x dma_request_sg_cnt: %x.\n", + bsg_job->request_payload.sg_cnt, sg_cnt); + rval = -EAGAIN; + goto done_unmap_sg; + } + + data_len = bsg_job->request_payload.payload_len; + mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, + &mgmt_dma, GFP_KERNEL); + if (!mgmt_b) { + ql_log(ql_log_warn, vha, 0x7042, + "DMA alloc failed for mgmt_b.\n"); + rval = -ENOMEM; + goto done_unmap_sg; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, mgmt_b, data_len); + + mn->options = cpu_to_le16(ACO_LOAD_MEMORY); + mn->parameter1 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); + break; + + case QLA84_MGMT_CHNG_CONFIG: + mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM); + mn->parameter1 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.id); + + mn->parameter2 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param0); + + mn->parameter3 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param1); + break; + + default: + rval = -EIO; + goto exit_mgmt; + } + + if (ql84_mgmt->mgmt.cmd != QLA84_MGMT_CHNG_CONFIG) { + mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->mgmt.len); + mn->dseg_count = cpu_to_le16(1); + mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma)); + mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma)); + mn->dseg_length = cpu_to_le32(ql84_mgmt->mgmt.len); + } + + rval = qla2x00_issue_iocb(vha, mn, mn_dma, 0); + + if (rval) { + ql_log(ql_log_warn, vha, 0x7043, + "Vendor request 84xx mgmt failed.\n"); + + rval = 0; + bsg_job->reply->result = (DID_ERROR << 16); + + } else { + ql_dbg(ql_dbg_user, vha, 0x7044, + "Vendor request 84xx mgmt completed.\n"); + + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->result = DID_OK; + + if ((ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) || + (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO)) { + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, mgmt_b, + data_len); + } + } + + bsg_job->job_done(bsg_job); + +done_unmap_sg: + if (mgmt_b) + dma_free_coherent(&ha->pdev->dev, data_len, mgmt_b, mgmt_dma); + + if (dma_direction == DMA_TO_DEVICE) + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + else if (dma_direction == DMA_FROM_DEVICE) + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + +exit_mgmt: + dma_pool_free(ha->s_dma_pool, mn, mn_dma); + + return rval; +} + +static int +qla24xx_iidma(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + int rval = 0; + struct qla_port_param *port_param = NULL; + fc_port_t *fcport = NULL; + uint16_t mb[MAILBOX_REGISTER_COUNT]; + uint8_t *rsp_ptr = NULL; + + if (!IS_IIDMA_CAPABLE(vha->hw)) { + ql_log(ql_log_info, vha, 0x7046, "iiDMA not supported.\n"); + return -EINVAL; + } + + port_param = (struct qla_port_param *)((char *)bsg_job->request + + sizeof(struct fc_bsg_request)); + if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) { + ql_log(ql_log_warn, vha, 0x7048, + "Invalid destination type.\n"); + return -EINVAL; + } + + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { + if (fcport->port_type != FCT_TARGET) + continue; + + if (memcmp(port_param->fc_scsi_addr.dest_addr.wwpn, + fcport->port_name, sizeof(fcport->port_name))) + continue; + break; + } + + if (!fcport) { + ql_log(ql_log_warn, vha, 0x7049, + "Failed to find port.\n"); + return -EINVAL; + } + + if (atomic_read(&fcport->state) != FCS_ONLINE) { + ql_log(ql_log_warn, vha, 0x704a, + "Port is not online.\n"); + return -EINVAL; + } + + if (fcport->flags & FCF_LOGIN_NEEDED) { + ql_log(ql_log_warn, vha, 0x704b, + "Remote port not logged in flags = 0x%x.\n", fcport->flags); + return -EINVAL; + } + + if (port_param->mode) + rval = qla2x00_set_idma_speed(vha, fcport->loop_id, + port_param->speed, mb); + else + rval = qla2x00_get_idma_speed(vha, fcport->loop_id, + &port_param->speed, mb); + + if (rval) { + ql_log(ql_log_warn, vha, 0x704c, + "iIDMA cmd failed for %02x%02x%02x%02x%02x%02x%02x%02x -- " + "%04x %x %04x %04x.\n", fcport->port_name[0], + fcport->port_name[1], fcport->port_name[2], + fcport->port_name[3], fcport->port_name[4], + fcport->port_name[5], fcport->port_name[6], + fcport->port_name[7], rval, fcport->fp_speed, mb[0], mb[1]); + rval = 0; + bsg_job->reply->result = (DID_ERROR << 16); + + } else { + if (!port_param->mode) { + bsg_job->reply_len = sizeof(struct fc_bsg_reply) + + sizeof(struct qla_port_param); + + rsp_ptr = ((uint8_t *)bsg_job->reply) + + sizeof(struct fc_bsg_reply); + + memcpy(rsp_ptr, port_param, + sizeof(struct qla_port_param)); + } + + bsg_job->reply->result = DID_OK; + } + + bsg_job->job_done(bsg_job); + return rval; +} + +static int +qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, scsi_qla_host_t *vha, + uint8_t is_update) +{ + uint32_t start = 0; + int valid = 0; + struct qla_hw_data *ha = vha->hw; + + if (unlikely(pci_channel_offline(ha->pdev))) + return -EINVAL; + + start = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + if (start > ha->optrom_size) { + ql_log(ql_log_warn, vha, 0x7055, + "start %d > optrom_size %d.\n", start, ha->optrom_size); + return -EINVAL; + } + + if (ha->optrom_state != QLA_SWAITING) { + ql_log(ql_log_info, vha, 0x7056, + "optrom_state %d.\n", ha->optrom_state); + return -EBUSY; + } + + ha->optrom_region_start = start; + ql_dbg(ql_dbg_user, vha, 0x7057, "is_update=%d.\n", is_update); + if (is_update) { + if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0) + valid = 1; + else if (start == (ha->flt_region_boot * 4) || + start == (ha->flt_region_fw * 4)) + valid = 1; + else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || + IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) + valid = 1; + if (!valid) { + ql_log(ql_log_warn, vha, 0x7058, + "Invalid start region 0x%x/0x%x.\n", start, + bsg_job->request_payload.payload_len); + return -EINVAL; + } + + ha->optrom_region_size = start + + bsg_job->request_payload.payload_len > ha->optrom_size ? + ha->optrom_size - start : + bsg_job->request_payload.payload_len; + ha->optrom_state = QLA_SWRITING; + } else { + ha->optrom_region_size = start + + bsg_job->reply_payload.payload_len > ha->optrom_size ? + ha->optrom_size - start : + bsg_job->reply_payload.payload_len; + ha->optrom_state = QLA_SREADING; + } + + ha->optrom_buffer = vmalloc(ha->optrom_region_size); + if (!ha->optrom_buffer) { + ql_log(ql_log_warn, vha, 0x7059, + "Read: Unable to allocate memory for optrom retrieval " + "(%x)\n", ha->optrom_region_size); + + ha->optrom_state = QLA_SWAITING; + return -ENOMEM; + } + + memset(ha->optrom_buffer, 0, ha->optrom_region_size); + return 0; +} + +static int +qla2x00_read_optrom(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + + if (ha->flags.isp82xx_reset_hdlr_active) + return -EBUSY; + + rval = qla2x00_optrom_setup(bsg_job, vha, 0); + if (rval) + return rval; + + ha->isp_ops->read_optrom(vha, ha->optrom_buffer, + ha->optrom_region_start, ha->optrom_region_size); + + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, ha->optrom_buffer, + ha->optrom_region_size); + + bsg_job->reply->reply_payload_rcv_len = ha->optrom_region_size; + bsg_job->reply->result = DID_OK; + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + bsg_job->job_done(bsg_job); + return rval; +} + +static int +qla2x00_update_optrom(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + + rval = qla2x00_optrom_setup(bsg_job, vha, 1); + if (rval) + return rval; + + /* Set the isp82xx_no_md_cap not to capture minidump */ + ha->flags.isp82xx_no_md_cap = 1; + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, ha->optrom_buffer, + ha->optrom_region_size); + + ha->isp_ops->write_optrom(vha, ha->optrom_buffer, + ha->optrom_region_start, ha->optrom_region_size); + + bsg_job->reply->result = DID_OK; + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + bsg_job->job_done(bsg_job); + return rval; +} + +static int +qla2x00_update_fru_versions(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + uint8_t bsg[DMA_POOL_SIZE]; + struct qla_image_version_list *list = (void *)bsg; + struct qla_image_version *image; + uint32_t count; + dma_addr_t sfp_dma; + void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + if (!sfp) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_NO_MEMORY; + goto done; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, list, sizeof(bsg)); + + image = list->version; + count = list->count; + while (count--) { + memcpy(sfp, &image->field_info, sizeof(image->field_info)); + rval = qla2x00_write_sfp(vha, sfp_dma, sfp, + image->field_address.device, image->field_address.offset, + sizeof(image->field_info), image->field_address.option); + if (rval) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + goto dealloc; + } + image++; + } + + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0; + +dealloc: + dma_pool_free(ha->s_dma_pool, sfp, sfp_dma); + +done: + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->result = DID_OK << 16; + bsg_job->job_done(bsg_job); + + return 0; +} + +static int +qla2x00_read_fru_status(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + uint8_t bsg[DMA_POOL_SIZE]; + struct qla_status_reg *sr = (void *)bsg; + dma_addr_t sfp_dma; + uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + if (!sfp) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_NO_MEMORY; + goto done; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, sr, sizeof(*sr)); + + rval = qla2x00_read_sfp(vha, sfp_dma, sfp, + sr->field_address.device, sr->field_address.offset, + sizeof(sr->status_reg), sr->field_address.option); + sr->status_reg = *sfp; + + if (rval) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + goto dealloc; + } + + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, sr, sizeof(*sr)); + + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0; + +dealloc: + dma_pool_free(ha->s_dma_pool, sfp, sfp_dma); + +done: + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->reply_payload_rcv_len = sizeof(*sr); + bsg_job->reply->result = DID_OK << 16; + bsg_job->job_done(bsg_job); + + return 0; +} + +static int +qla2x00_write_fru_status(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + uint8_t bsg[DMA_POOL_SIZE]; + struct qla_status_reg *sr = (void *)bsg; + dma_addr_t sfp_dma; + uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + if (!sfp) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_NO_MEMORY; + goto done; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, sr, sizeof(*sr)); + + *sfp = sr->status_reg; + rval = qla2x00_write_sfp(vha, sfp_dma, sfp, + sr->field_address.device, sr->field_address.offset, + sizeof(sr->status_reg), sr->field_address.option); + + if (rval) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + goto dealloc; + } + + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0; + +dealloc: + dma_pool_free(ha->s_dma_pool, sfp, sfp_dma); + +done: + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->result = DID_OK << 16; + bsg_job->job_done(bsg_job); + + return 0; +} + +static int +qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) +{ + switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) { + case QL_VND_LOOPBACK: + return qla2x00_process_loopback(bsg_job); + + case QL_VND_A84_RESET: + return qla84xx_reset(bsg_job); + + case QL_VND_A84_UPDATE_FW: + return qla84xx_updatefw(bsg_job); + + case QL_VND_A84_MGMT_CMD: + return qla84xx_mgmt_cmd(bsg_job); + + case QL_VND_IIDMA: + return qla24xx_iidma(bsg_job); + + case QL_VND_FCP_PRIO_CFG_CMD: + return qla24xx_proc_fcp_prio_cfg_cmd(bsg_job); + + case QL_VND_READ_FLASH: + return qla2x00_read_optrom(bsg_job); + + case QL_VND_UPDATE_FLASH: + return qla2x00_update_optrom(bsg_job); + + case QL_VND_SET_FRU_VERSION: + return qla2x00_update_fru_versions(bsg_job); + + case QL_VND_READ_FRU_STATUS: + return qla2x00_read_fru_status(bsg_job); + + case QL_VND_WRITE_FRU_STATUS: + return qla2x00_write_fru_status(bsg_job); + + default: + bsg_job->reply->result = (DID_ERROR << 16); + bsg_job->job_done(bsg_job); + return -ENOSYS; + } +} + +int +qla24xx_bsg_request(struct fc_bsg_job *bsg_job) +{ + int ret = -EINVAL; + struct fc_rport *rport; + fc_port_t *fcport = NULL; + struct Scsi_Host *host; + scsi_qla_host_t *vha; + + /* In case no data transferred. */ + bsg_job->reply->reply_payload_rcv_len = 0; + + if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { + rport = bsg_job->rport; + fcport = *(fc_port_t **) rport->dd_data; + host = rport_to_shost(rport); + vha = shost_priv(host); + } else { + host = bsg_job->shost; + vha = shost_priv(host); + } + + if (qla2x00_reset_active(vha)) { + ql_dbg(ql_dbg_user, vha, 0x709f, + "BSG: ISP abort active/needed -- cmd=%d.\n", + bsg_job->request->msgcode); + bsg_job->reply->result = (DID_ERROR << 16); + bsg_job->job_done(bsg_job); + return -EBUSY; + } + + ql_dbg(ql_dbg_user, vha, 0x7000, + "Entered %s msgcode=0x%x.\n", __func__, bsg_job->request->msgcode); + + switch (bsg_job->request->msgcode) { + case FC_BSG_RPT_ELS: + case FC_BSG_HST_ELS_NOLOGIN: + ret = qla2x00_process_els(bsg_job); + break; + case FC_BSG_HST_CT: + ret = qla2x00_process_ct(bsg_job); + break; + case FC_BSG_HST_VENDOR: + ret = qla2x00_process_vendor_specific(bsg_job); + break; + case FC_BSG_HST_ADD_RPORT: + case FC_BSG_HST_DEL_RPORT: + case FC_BSG_RPT_CT: + default: + ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n"); + bsg_job->reply->result = ret; + break; + } + return ret; +} + +int +qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) +{ + scsi_qla_host_t *vha = shost_priv(bsg_job->shost); + struct qla_hw_data *ha = vha->hw; + srb_t *sp; + int cnt, que; + unsigned long flags; + struct req_que *req; + + /* find the bsg job from the active list of commands */ + spin_lock_irqsave(&ha->hardware_lock, flags); + for (que = 0; que < ha->max_req_queues; que++) { + req = ha->req_q_map[que]; + if (!req) + continue; + + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + sp = req->outstanding_cmds[cnt]; + if (sp) { + if (((sp->type == SRB_CT_CMD) || + (sp->type == SRB_ELS_CMD_HST)) + && (sp->u.bsg_job == bsg_job)) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (ha->isp_ops->abort_command(sp)) { + ql_log(ql_log_warn, vha, 0x7089, + "mbx abort_command " + "failed.\n"); + bsg_job->req->errors = + bsg_job->reply->result = -EIO; + } else { + ql_dbg(ql_dbg_user, vha, 0x708a, + "mbx abort_command " + "success.\n"); + bsg_job->req->errors = + bsg_job->reply->result = 0; + } + spin_lock_irqsave(&ha->hardware_lock, flags); + goto done; + } + } + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + ql_log(ql_log_info, vha, 0x708b, "SRB not found to abort.\n"); + bsg_job->req->errors = bsg_job->reply->result = -ENXIO; + return 0; + +done: + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (bsg_job->request->msgcode == FC_BSG_HST_CT) + kfree(sp->fcport); + mempool_free(sp, ha->srb_mempool); + return 0; +} diff --git a/qla2x00t/qla_bsg.h b/qla2x00t/qla_bsg.h new file mode 100644 index 000000000..70caa63a8 --- /dev/null +++ b/qla2x00t/qla_bsg.h @@ -0,0 +1,186 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2011 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#ifndef __QLA_BSG_H +#define __QLA_BSG_H + +/* BSG Vendor specific commands */ +#define QL_VND_LOOPBACK 0x01 +#define QL_VND_A84_RESET 0x02 +#define QL_VND_A84_UPDATE_FW 0x03 +#define QL_VND_A84_MGMT_CMD 0x04 +#define QL_VND_IIDMA 0x05 +#define QL_VND_FCP_PRIO_CFG_CMD 0x06 +#define QL_VND_READ_FLASH 0x07 +#define QL_VND_UPDATE_FLASH 0x08 +#define QL_VND_SET_FRU_VERSION 0x0B +#define QL_VND_READ_FRU_STATUS 0x0C +#define QL_VND_WRITE_FRU_STATUS 0x0D + +/* BSG Vendor specific subcode returns */ +#define EXT_STATUS_OK 0 +#define EXT_STATUS_ERR 1 +#define EXT_STATUS_INVALID_PARAM 6 +#define EXT_STATUS_MAILBOX 11 +#define EXT_STATUS_NO_MEMORY 17 + +/* BSG definations for interpreting CommandSent field */ +#define INT_DEF_LB_LOOPBACK_CMD 0 +#define INT_DEF_LB_ECHO_CMD 1 + +/* Loopback related definations */ +#define EXTERNAL_LOOPBACK 0xF2 +#define ENABLE_INTERNAL_LOOPBACK 0x02 +#define INTERNAL_LOOPBACK_MASK 0x000E +#define MAX_ELS_FRAME_PAYLOAD 252 +#define ELS_OPCODE_BYTE 0x10 + +/* BSG Vendor specific definations */ +#define A84_ISSUE_WRITE_TYPE_CMD 0 +#define A84_ISSUE_READ_TYPE_CMD 1 +#define A84_CLEANUP_CMD 2 +#define A84_ISSUE_RESET_OP_FW 3 +#define A84_ISSUE_RESET_DIAG_FW 4 +#define A84_ISSUE_UPDATE_OPFW_CMD 5 +#define A84_ISSUE_UPDATE_DIAGFW_CMD 6 + +struct qla84_mgmt_param { + union { + struct { + uint32_t start_addr; + } mem; /* for QLA84_MGMT_READ/WRITE_MEM */ + struct { + uint32_t id; +#define QLA84_MGMT_CONFIG_ID_UIF 1 +#define QLA84_MGMT_CONFIG_ID_FCOE_COS 2 +#define QLA84_MGMT_CONFIG_ID_PAUSE 3 +#define QLA84_MGMT_CONFIG_ID_TIMEOUTS 4 + + uint32_t param0; + uint32_t param1; + } config; /* for QLA84_MGMT_CHNG_CONFIG */ + + struct { + uint32_t type; +#define QLA84_MGMT_INFO_CONFIG_LOG_DATA 1 /* Get Config Log Data */ +#define QLA84_MGMT_INFO_LOG_DATA 2 /* Get Log Data */ +#define QLA84_MGMT_INFO_PORT_STAT 3 /* Get Port Statistics */ +#define QLA84_MGMT_INFO_LIF_STAT 4 /* Get LIF Statistics */ +#define QLA84_MGMT_INFO_ASIC_STAT 5 /* Get ASIC Statistics */ +#define QLA84_MGMT_INFO_CONFIG_PARAMS 6 /* Get Config Parameters */ +#define QLA84_MGMT_INFO_PANIC_LOG 7 /* Get Panic Log */ + + uint32_t context; +/* +* context definitions for QLA84_MGMT_INFO_CONFIG_LOG_DATA +*/ +#define IC_LOG_DATA_LOG_ID_DEBUG_LOG 0 +#define IC_LOG_DATA_LOG_ID_LEARN_LOG 1 +#define IC_LOG_DATA_LOG_ID_FC_ACL_INGRESS_LOG 2 +#define IC_LOG_DATA_LOG_ID_FC_ACL_EGRESS_LOG 3 +#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_INGRESS_LOG 4 +#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_EGRESS_LOG 5 +#define IC_LOG_DATA_LOG_ID_MESSAGE_TRANSMIT_LOG 6 +#define IC_LOG_DATA_LOG_ID_MESSAGE_RECEIVE_LOG 7 +#define IC_LOG_DATA_LOG_ID_LINK_EVENT_LOG 8 +#define IC_LOG_DATA_LOG_ID_DCX_LOG 9 + +/* +* context definitions for QLA84_MGMT_INFO_PORT_STAT +*/ +#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT0 0 +#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT1 1 +#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT0 2 +#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT1 3 +#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT0 4 +#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT1 5 + + +/* +* context definitions for QLA84_MGMT_INFO_LIF_STAT +*/ +#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT0 0 +#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT1 1 +#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT0 2 +#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT1 3 +#define IC_LIF_STATISTICS_LIF_NUMBER_CPU 6 + + } info; /* for QLA84_MGMT_GET_INFO */ + } u; +}; + +struct qla84_msg_mgmt { + uint16_t cmd; +#define QLA84_MGMT_READ_MEM 0x00 +#define QLA84_MGMT_WRITE_MEM 0x01 +#define QLA84_MGMT_CHNG_CONFIG 0x02 +#define QLA84_MGMT_GET_INFO 0x03 + uint16_t rsrvd; + struct qla84_mgmt_param mgmtp;/* parameters for cmd */ + uint32_t len; /* bytes in payload following this struct */ + uint8_t payload[0]; /* payload for cmd */ +}; + +struct qla_bsg_a84_mgmt { + struct qla84_msg_mgmt mgmt; +} __attribute__ ((packed)); + +struct qla_scsi_addr { + uint16_t bus; + uint16_t target; +} __attribute__ ((packed)); + +struct qla_ext_dest_addr { + union { + uint8_t wwnn[8]; + uint8_t wwpn[8]; + uint8_t id[4]; + struct qla_scsi_addr scsi_addr; + } dest_addr; + uint16_t dest_type; +#define EXT_DEF_TYPE_WWPN 2 + uint16_t lun; + uint16_t padding[2]; +} __attribute__ ((packed)); + +struct qla_port_param { + struct qla_ext_dest_addr fc_scsi_addr; + uint16_t mode; + uint16_t speed; +} __attribute__ ((packed)); + + +/* FRU VPD */ + +#define MAX_FRU_SIZE 36 + +struct qla_field_address { + uint16_t offset; + uint16_t device; + uint16_t option; +} __packed; + +struct qla_field_info { + uint8_t version[MAX_FRU_SIZE]; +} __packed; + +struct qla_image_version { + struct qla_field_address field_address; + struct qla_field_info field_info; +} __packed; + +struct qla_image_version_list { + uint32_t count; + struct qla_image_version version[0]; +} __packed; + +struct qla_status_reg { + struct qla_field_address field_address; + uint8_t status_reg; + uint8_t reserved[7]; +} __packed; + +#endif diff --git a/qla2x00t/qla_dbg.c b/qla2x00t/qla_dbg.c index cbef78576..9420c7f3c 100644 --- a/qla2x00t/qla_dbg.c +++ b/qla2x00t/qla_dbg.c @@ -1,15 +1,47 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ + +/* + * Table for showing the current message id in use for particular level + * Change this table for addition of log/debug messages. + * ---------------------------------------------------------------------- + * | Level | Last Value Used | Holes | + * ---------------------------------------------------------------------- + * | Module Init and Probe | 0x0120 | 0x4b,0xba,0xfa | + * | Mailbox commands | 0x113e | 0x111a-0x111b | + * | Device Discovery | 0x2086 | 0x2020-0x2022 | + * | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 | + * | | | 0x302d,0x302e | + * | DPC Thread | 0x401c | 0x4002,0x4013 | + * | Async Events | 0x505f | 0x502b-0x502f | + * | | | 0x5047,0x5052 | + * | Timer Routines | 0x6011 | | + * | User Space Interactions | 0x709f | 0x7018,0x702e | + * | | | 0x7039,0x7045 | + * | | | 0x7073-0x7075 | + * | | | 0x708c | + * | Task Management | 0x803c | 0x8025-0x8026 | + * | | | 0x800b,0x8039 | + * | AER/EEH | 0x9011 | | + * | Virtual Port | 0xa008 | | + * | ISP82XX Specific | 0xb054 | 0xb024 | + * | MultiQ | 0xc00c | | + * | Misc | 0xd010 | | + * ---------------------------------------------------------------------- + */ + #include "qla_def.h" #include +static uint32_t ql_dbg_offset = 0x800; + static inline void -qla2xxx_prep_dump(scsi_qla_host_t *ha, struct qla2xxx_fw_dump *fw_dump) +qla2xxx_prep_dump(struct qla_hw_data *ha, struct qla2xxx_fw_dump *fw_dump) { fw_dump->fw_major_version = htonl(ha->fw_major_version); fw_dump->fw_minor_version = htonl(ha->fw_minor_version); @@ -23,22 +55,24 @@ qla2xxx_prep_dump(scsi_qla_host_t *ha, struct qla2xxx_fw_dump *fw_dump) } static inline void * -qla2xxx_copy_queues(scsi_qla_host_t *ha, void *ptr) +qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr) { + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; /* Request queue. */ - memcpy(ptr, ha->request_ring, ha->request_q_length * + memcpy(ptr, req->ring, req->length * sizeof(request_t)); /* Response queue. */ - ptr += ha->request_q_length * sizeof(request_t); - memcpy(ptr, ha->response_ring, ha->response_q_length * + ptr += req->length * sizeof(request_t); + memcpy(ptr, rsp->ring, rsp->length * sizeof(response_t)); - return ptr + (ha->response_q_length * sizeof(response_t)); + return ptr + (rsp->length * sizeof(response_t)); } static int -qla24xx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint32_t *ram, +qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, uint32_t ram_dwords, void **nxt) { int rval; @@ -54,7 +88,7 @@ qla24xx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint32_t *ram, WRT_REG_WORD(®->mailbox0, MBC_DUMP_RISC_RAM_EXTENDED); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); - dwords = GID_LIST_SIZE / 4; + dwords = qla2x00_gid_list_size(ha) / 4; for (cnt = 0; cnt < ram_dwords && rval == QLA_SUCCESS; cnt += dwords, addr += dwords) { if (cnt + dwords > ram_dwords) @@ -112,7 +146,7 @@ qla24xx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint32_t *ram, } static int -qla24xx_dump_memory(scsi_qla_host_t *ha, uint32_t *code_ram, +qla24xx_dump_memory(struct qla_hw_data *ha, uint32_t *code_ram, uint32_t cram_size, void **nxt) { int rval; @@ -147,11 +181,9 @@ qla24xx_pause_risc(struct device_reg_24xx __iomem *reg) int rval = QLA_SUCCESS; uint32_t cnt; - if (RD_REG_DWORD(®->hccr) & HCCRX_RISC_PAUSE) - return rval; - WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_PAUSE); - for (cnt = 30000; (RD_REG_DWORD(®->hccr) & HCCRX_RISC_PAUSE) == 0 && + for (cnt = 30000; + ((RD_REG_DWORD(®->host_status) & HSRX_RISC_PAUSED) == 0) && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); @@ -163,7 +195,7 @@ qla24xx_pause_risc(struct device_reg_24xx __iomem *reg) } static int -qla24xx_soft_reset(scsi_qla_host_t *ha) +qla24xx_soft_reset(struct qla_hw_data *ha) { int rval = QLA_SUCCESS; uint32_t cnt; @@ -215,8 +247,8 @@ qla24xx_soft_reset(scsi_qla_host_t *ha) } static int -qla2xxx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint16_t *ram, - uint16_t ram_words, void **nxt) +qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram, + uint32_t ram_words, void **nxt) { int rval; uint32_t cnt, stat, timer, words, idx; @@ -231,7 +263,7 @@ qla2xxx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint16_t *ram, WRT_MAILBOX_REG(ha, reg, 0, MBC_DUMP_RISC_RAM_EXTENDED); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); - words = GID_LIST_SIZE / 2; + words = qla2x00_gid_list_size(ha) / 2; for (cnt = 0; cnt < ram_words && rval == QLA_SUCCESS; cnt += words, addr += words) { if (cnt + words > ram_words) @@ -308,22 +340,181 @@ qla2xxx_read_window(struct device_reg_2xxx __iomem *reg, uint32_t count, *buf++ = htons(RD_REG_WORD(dmp_reg++)); } +static inline void * +qla24xx_copy_eft(struct qla_hw_data *ha, void *ptr) +{ + if (!ha->eft) + return ptr; + + memcpy(ptr, ha->eft, ntohl(ha->fw_dump->eft_size)); + return ptr + ntohl(ha->fw_dump->eft_size); +} + +static inline void * +qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) +{ + uint32_t cnt; + uint32_t *iter_reg; + struct qla2xxx_fce_chain *fcec = ptr; + + if (!ha->fce) + return ptr; + + *last_chain = &fcec->type; + fcec->type = __constant_htonl(DUMP_CHAIN_FCE); + fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) + + fce_calc_size(ha->fce_bufs)); + fcec->size = htonl(fce_calc_size(ha->fce_bufs)); + fcec->addr_l = htonl(LSD(ha->fce_dma)); + fcec->addr_h = htonl(MSD(ha->fce_dma)); + + iter_reg = fcec->eregs; + for (cnt = 0; cnt < 8; cnt++) + *iter_reg++ = htonl(ha->fce_mb[cnt]); + + memcpy(iter_reg, ha->fce, ntohl(fcec->size)); + + return (void *)iter_reg + ntohl(fcec->size); +} + +static inline void * +qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) +{ + struct qla2xxx_mqueue_chain *q; + struct qla2xxx_mqueue_header *qh; + struct req_que *req; + struct rsp_que *rsp; + int que; + + if (!ha->mqenable) + return ptr; + + /* Request queues */ + for (que = 1; que < ha->max_req_queues; que++) { + req = ha->req_q_map[que]; + if (!req) + break; + + /* Add chain. */ + q = ptr; + *last_chain = &q->type; + q->type = __constant_htonl(DUMP_CHAIN_QUEUE); + q->chain_size = htonl( + sizeof(struct qla2xxx_mqueue_chain) + + sizeof(struct qla2xxx_mqueue_header) + + (req->length * sizeof(request_t))); + ptr += sizeof(struct qla2xxx_mqueue_chain); + + /* Add header. */ + qh = ptr; + qh->queue = __constant_htonl(TYPE_REQUEST_QUEUE); + qh->number = htonl(que); + qh->size = htonl(req->length * sizeof(request_t)); + ptr += sizeof(struct qla2xxx_mqueue_header); + + /* Add data. */ + memcpy(ptr, req->ring, req->length * sizeof(request_t)); + ptr += req->length * sizeof(request_t); + } + + /* Response queues */ + for (que = 1; que < ha->max_rsp_queues; que++) { + rsp = ha->rsp_q_map[que]; + if (!rsp) + break; + + /* Add chain. */ + q = ptr; + *last_chain = &q->type; + q->type = __constant_htonl(DUMP_CHAIN_QUEUE); + q->chain_size = htonl( + sizeof(struct qla2xxx_mqueue_chain) + + sizeof(struct qla2xxx_mqueue_header) + + (rsp->length * sizeof(response_t))); + ptr += sizeof(struct qla2xxx_mqueue_chain); + + /* Add header. */ + qh = ptr; + qh->queue = __constant_htonl(TYPE_RESPONSE_QUEUE); + qh->number = htonl(que); + qh->size = htonl(rsp->length * sizeof(response_t)); + ptr += sizeof(struct qla2xxx_mqueue_header); + + /* Add data. */ + memcpy(ptr, rsp->ring, rsp->length * sizeof(response_t)); + ptr += rsp->length * sizeof(response_t); + } + + return ptr; +} + +static inline void * +qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) +{ + uint32_t cnt, que_idx; + uint8_t que_cnt; + struct qla2xxx_mq_chain *mq = ptr; + struct device_reg_25xxmq __iomem *reg; + + if (!ha->mqenable || IS_QLA83XX(ha)) + return ptr; + + mq = ptr; + *last_chain = &mq->type; + mq->type = __constant_htonl(DUMP_CHAIN_MQ); + mq->chain_size = __constant_htonl(sizeof(struct qla2xxx_mq_chain)); + + que_cnt = ha->max_req_queues > ha->max_rsp_queues ? + ha->max_req_queues : ha->max_rsp_queues; + mq->count = htonl(que_cnt); + for (cnt = 0; cnt < que_cnt; cnt++) { + reg = (struct device_reg_25xxmq *) ((void *) + ha->mqiobase + cnt * QLA_QUE_PAGE); + que_idx = cnt * 4; + mq->qregs[que_idx] = htonl(RD_REG_DWORD(®->req_q_in)); + mq->qregs[que_idx+1] = htonl(RD_REG_DWORD(®->req_q_out)); + mq->qregs[que_idx+2] = htonl(RD_REG_DWORD(®->rsp_q_in)); + mq->qregs[que_idx+3] = htonl(RD_REG_DWORD(®->rsp_q_out)); + } + + return ptr + sizeof(struct qla2xxx_mq_chain); +} + +void +qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval) +{ + struct qla_hw_data *ha = vha->hw; + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xd000, + "Failed to dump firmware (%x).\n", rval); + ha->fw_dumped = 0; + } else { + ql_log(ql_log_info, vha, 0xd001, + "Firmware dump saved to temp buffer (%ld/%p).\n", + vha->host_no, ha->fw_dump); + ha->fw_dumped = 1; + qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP); + } +} + /** * qla2300_fw_dump() - Dumps binary data from the 2300 firmware. * @ha: HA context * @hardware_locked: Called with the hardware_lock */ void -qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked) +qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; uint32_t cnt; - + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint16_t __iomem *dmp_reg; unsigned long flags; struct qla2300_fw_dump *fw; void *nxt; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); flags = 0; @@ -331,15 +522,16 @@ qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd002, + "No buffer available for dump.\n"); goto qla2300_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + ql_log(ql_log_warn, vha, 0xd003, + "Firmware has been previously dumped (%p) " + "-- ignoring request.\n", + ha->fw_dump); goto qla2300_fw_dump_failed; } fw = &ha->fw_dump->isp.isp23; @@ -460,17 +652,7 @@ qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked) if (rval == QLA_SUCCESS) qla2xxx_copy_queues(ha, nxt); - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - ha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla2300_fw_dump_failed: if (!hardware_locked) @@ -483,16 +665,18 @@ qla2300_fw_dump_failed: * @hardware_locked: Called with the hardware_lock */ void -qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked) +qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; uint32_t cnt, timer; uint16_t risc_address; uint16_t mb0, mb2; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint16_t __iomem *dmp_reg; unsigned long flags; struct qla2100_fw_dump *fw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); risc_address = 0; mb0 = mb2 = 0; @@ -502,15 +686,16 @@ qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd004, + "No buffer available for dump.\n"); goto qla2100_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + ql_log(ql_log_warn, vha, 0xd005, + "Firmware has been previously dumped (%p) " + "-- ignoring request.\n", + ha->fw_dump); goto qla2100_fw_dump_failed; } fw = &ha->fw_dump->isp.isp21; @@ -662,20 +847,15 @@ qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked) } } +#ifdef __COVERITY__ + BUG_ON(cnt * sizeof(fw->risc_ram[0]) + 2 * sizeof(request_t) > + sizeof(fw->risc_ram)); +#endif + if (rval == QLA_SUCCESS) qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]); - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - ha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla2100_fw_dump_failed: if (!hardware_locked) @@ -683,12 +863,12 @@ qla2100_fw_dump_failed: } void -qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) +qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; uint32_t cnt; uint32_t risc_address; - + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; uint32_t *iter_reg; @@ -697,6 +877,10 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) struct qla24xx_fw_dump *fw; uint32_t ext_mem_cnt; void *nxt; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + + if (IS_QLA82XX(ha)) + return; risc_address = ext_mem_cnt = 0; flags = 0; @@ -705,15 +889,16 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd006, + "No buffer available for dump.\n"); goto qla24xx_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + ql_log(ql_log_warn, vha, 0xd007, + "Firmware has been previously dumped (%p) " + "-- ignoring request.\n", + ha->fw_dump); goto qla24xx_fw_dump_failed; } fw = &ha->fw_dump->isp.isp24; @@ -907,21 +1092,11 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) goto qla24xx_fw_dump_failed_0; nxt = qla2xxx_copy_queues(ha, nxt); - if (ha->eft) - memcpy(nxt, ha->eft, ntohl(ha->fw_dump->eft_size)); + + qla24xx_copy_eft(ha, nxt); qla24xx_fw_dump_failed_0: - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - ha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla24xx_fw_dump_failed: if (!hardware_locked) @@ -929,12 +1104,12 @@ qla24xx_fw_dump_failed: } void -qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) +qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; uint32_t cnt; uint32_t risc_address; - + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; uint32_t *iter_reg; @@ -942,8 +1117,9 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) unsigned long flags; struct qla25xx_fw_dump *fw; uint32_t ext_mem_cnt; - void *nxt; - struct qla2xxx_fce_chain *fcec; + void *nxt, *nxt_chain; + uint32_t *last_chain = NULL; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); risc_address = ext_mem_cnt = 0; flags = 0; @@ -952,15 +1128,16 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd008, + "No buffer available for dump.\n"); goto qla25xx_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + ql_log(ql_log_warn, vha, 0xd009, + "Firmware has been previously dumped (%p) " + "-- ignoring request.\n", + ha->fw_dump); goto qla25xx_fw_dump_failed; } fw = &ha->fw_dump->isp.isp25; @@ -988,6 +1165,7 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++)); fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg)); fw->pcie_regs[3] = htonl(RD_REG_DWORD(®->iobase_window)); + WRT_REG_DWORD(®->iobase_window, 0x00); RD_REG_DWORD(®->iobase_window); @@ -1205,6 +1383,10 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg); qla24xx_read_window(reg, 0x6F00, 16, iter_reg); + /* Multi queue registers */ + nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset, + &last_chain); + rval = qla24xx_soft_reset(ha); if (rval != QLA_SUCCESS) goto qla25xx_fw_dump_failed_0; @@ -1214,85 +1396,1085 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) if (rval != QLA_SUCCESS) goto qla25xx_fw_dump_failed_0; - /* Fibre Channel Trace Buffer. */ nxt = qla2xxx_copy_queues(ha, nxt); - if (ha->eft) - memcpy(nxt, ha->eft, ntohl(ha->fw_dump->eft_size)); - /* Fibre Channel Event Buffer. */ - if (!ha->fce) - goto qla25xx_fw_dump_failed_0; + qla24xx_copy_eft(ha, nxt); - ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); - - fcec = nxt + ntohl(ha->fw_dump->eft_size); - fcec->type = __constant_htonl(DUMP_CHAIN_FCE | DUMP_CHAIN_LAST); - fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) + - fce_calc_size(ha->fce_bufs)); - fcec->size = htonl(fce_calc_size(ha->fce_bufs)); - fcec->addr_l = htonl(LSD(ha->fce_dma)); - fcec->addr_h = htonl(MSD(ha->fce_dma)); - - iter_reg = fcec->eregs; - for (cnt = 0; cnt < 8; cnt++) - *iter_reg++ = htonl(ha->fce_mb[cnt]); - - memcpy(iter_reg, ha->fce, ntohl(fcec->size)); - -qla25xx_fw_dump_failed_0: - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - ha->host_no, ha->fw_dump); - ha->fw_dumped = 1; + /* Chain entries -- started with MQ. */ + nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain); + nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); + if (last_chain) { + ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); + *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); } + /* Adjust valid length. */ + ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump); + +qla25xx_fw_dump_failed_0: + qla2xxx_dump_post_process(base_vha, rval); + qla25xx_fw_dump_failed: if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); } +void +qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) +{ + int rval; + uint32_t cnt; + uint32_t risc_address; + struct qla_hw_data *ha = vha->hw; + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + uint32_t __iomem *dmp_reg; + uint32_t *iter_reg; + uint16_t __iomem *mbx_reg; + unsigned long flags; + struct qla81xx_fw_dump *fw; + uint32_t ext_mem_cnt; + void *nxt, *nxt_chain; + uint32_t *last_chain = NULL; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + + risc_address = ext_mem_cnt = 0; + flags = 0; + + if (!hardware_locked) + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (!ha->fw_dump) { + ql_log(ql_log_warn, vha, 0xd00a, + "No buffer available for dump.\n"); + goto qla81xx_fw_dump_failed; + } + + if (ha->fw_dumped) { + ql_log(ql_log_warn, vha, 0xd00b, + "Firmware has been previously dumped (%p) " + "-- ignoring request.\n", + ha->fw_dump); + goto qla81xx_fw_dump_failed; + } + fw = &ha->fw_dump->isp.isp81; + qla2xxx_prep_dump(ha, ha->fw_dump); + + fw->host_status = htonl(RD_REG_DWORD(®->host_status)); + + /* Pause RISC. */ + rval = qla24xx_pause_risc(reg); + if (rval != QLA_SUCCESS) + goto qla81xx_fw_dump_failed_0; + + /* Host/Risc registers. */ + iter_reg = fw->host_risc_reg; + iter_reg = qla24xx_read_window(reg, 0x7000, 16, iter_reg); + qla24xx_read_window(reg, 0x7010, 16, iter_reg); + + /* PCIe registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x7C00); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_window, 0x01); + dmp_reg = ®->iobase_c4; + fw->pcie_regs[0] = htonl(RD_REG_DWORD(dmp_reg++)); + fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++)); + fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg)); + fw->pcie_regs[3] = htonl(RD_REG_DWORD(®->iobase_window)); + + WRT_REG_DWORD(®->iobase_window, 0x00); + RD_REG_DWORD(®->iobase_window); + + /* Host interface registers. */ + dmp_reg = ®->flash_addr; + for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++) + fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Disable interrupts. */ + WRT_REG_DWORD(®->ictrl, 0); + RD_REG_DWORD(®->ictrl); + + /* Shadow registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x0F70); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_select, 0xB0000000); + fw->shadow_reg[0] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0100000); + fw->shadow_reg[1] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0200000); + fw->shadow_reg[2] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0300000); + fw->shadow_reg[3] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0400000); + fw->shadow_reg[4] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0500000); + fw->shadow_reg[5] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0600000); + fw->shadow_reg[6] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0700000); + fw->shadow_reg[7] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0800000); + fw->shadow_reg[8] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0900000); + fw->shadow_reg[9] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0A00000); + fw->shadow_reg[10] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + /* RISC I/O register. */ + WRT_REG_DWORD(®->iobase_addr, 0x0010); + fw->risc_io_reg = htonl(RD_REG_DWORD(®->iobase_window)); + + /* Mailbox registers. */ + mbx_reg = ®->mailbox0; + for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++) + fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++)); + + /* Transfer sequence registers. */ + iter_reg = fw->xseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg); + qla24xx_read_window(reg, 0xBF70, 16, iter_reg); + + iter_reg = fw->xseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xBFC0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBFD0, 16, iter_reg); + qla24xx_read_window(reg, 0xBFE0, 16, iter_reg); + + qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg); + + /* Receive sequence registers. */ + iter_reg = fw->rseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg); + qla24xx_read_window(reg, 0xFF70, 16, iter_reg); + + iter_reg = fw->rseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xFFC0, 16, iter_reg); + qla24xx_read_window(reg, 0xFFD0, 16, iter_reg); + + qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg); + qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg); + + /* Auxiliary sequence registers. */ + iter_reg = fw->aseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xB000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB060, 16, iter_reg); + qla24xx_read_window(reg, 0xB070, 16, iter_reg); + + iter_reg = fw->aseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xB0C0, 16, iter_reg); + qla24xx_read_window(reg, 0xB0D0, 16, iter_reg); + + qla24xx_read_window(reg, 0xB0E0, 16, fw->aseq_1_reg); + qla24xx_read_window(reg, 0xB0F0, 16, fw->aseq_2_reg); + + /* Command DMA registers. */ + qla24xx_read_window(reg, 0x7100, 16, fw->cmd_dma_reg); + + /* Queues. */ + iter_reg = fw->req0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->resp0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->req1_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Transmit DMA registers. */ + iter_reg = fw->xmt0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg); + qla24xx_read_window(reg, 0x7610, 16, iter_reg); + + iter_reg = fw->xmt1_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg); + qla24xx_read_window(reg, 0x7630, 16, iter_reg); + + iter_reg = fw->xmt2_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg); + qla24xx_read_window(reg, 0x7650, 16, iter_reg); + + iter_reg = fw->xmt3_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg); + qla24xx_read_window(reg, 0x7670, 16, iter_reg); + + iter_reg = fw->xmt4_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg); + qla24xx_read_window(reg, 0x7690, 16, iter_reg); + + qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg); + + /* Receive DMA registers. */ + iter_reg = fw->rcvt0_data_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg); + qla24xx_read_window(reg, 0x7710, 16, iter_reg); + + iter_reg = fw->rcvt1_data_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg); + qla24xx_read_window(reg, 0x7730, 16, iter_reg); + + /* RISC registers. */ + iter_reg = fw->risc_gp_reg; + iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg); + qla24xx_read_window(reg, 0x0F70, 16, iter_reg); + + /* Local memory controller registers. */ + iter_reg = fw->lmc_reg; + iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3060, 16, iter_reg); + qla24xx_read_window(reg, 0x3070, 16, iter_reg); + + /* Fibre Protocol Module registers. */ + iter_reg = fw->fpm_hdw_reg; + iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40C0, 16, iter_reg); + qla24xx_read_window(reg, 0x40D0, 16, iter_reg); + + /* Frame Buffer registers. */ + iter_reg = fw->fb_hdw_reg; + iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x61C0, 16, iter_reg); + qla24xx_read_window(reg, 0x6F00, 16, iter_reg); + + /* Multi queue registers */ + nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset, + &last_chain); + + rval = qla24xx_soft_reset(ha); + if (rval != QLA_SUCCESS) + goto qla81xx_fw_dump_failed_0; + + rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram), + &nxt); + if (rval != QLA_SUCCESS) + goto qla81xx_fw_dump_failed_0; + + nxt = qla2xxx_copy_queues(ha, nxt); + + qla24xx_copy_eft(ha, nxt); + + /* Chain entries -- started with MQ. */ + nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain); + nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); + if (last_chain) { + ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); + *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + } + + /* Adjust valid length. */ + ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump); + +qla81xx_fw_dump_failed_0: + qla2xxx_dump_post_process(base_vha, rval); + +qla81xx_fw_dump_failed: + if (!hardware_locked) + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void +qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) +{ + int rval; + uint32_t cnt, reg_data; + uint32_t risc_address; + struct qla_hw_data *ha = vha->hw; + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + uint32_t __iomem *dmp_reg; + uint32_t *iter_reg; + uint16_t __iomem *mbx_reg; + unsigned long flags; + struct qla83xx_fw_dump *fw; + uint32_t ext_mem_cnt; + void *nxt, *nxt_chain; + uint32_t *last_chain = NULL; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + + risc_address = ext_mem_cnt = 0; + flags = 0; + + if (!hardware_locked) + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (!ha->fw_dump) { + ql_log(ql_log_warn, vha, 0xd00c, + "No buffer available for dump!!!\n"); + goto qla83xx_fw_dump_failed; + } + + if (ha->fw_dumped) { + ql_log(ql_log_warn, vha, 0xd00d, + "Firmware has been previously dumped (%p) -- ignoring " + "request...\n", ha->fw_dump); + goto qla83xx_fw_dump_failed; + } + fw = &ha->fw_dump->isp.isp83; + qla2xxx_prep_dump(ha, ha->fw_dump); + + fw->host_status = htonl(RD_REG_DWORD(®->host_status)); + + /* Pause RISC. */ + rval = qla24xx_pause_risc(reg); + if (rval != QLA_SUCCESS) + goto qla83xx_fw_dump_failed_0; + + WRT_REG_DWORD(®->iobase_addr, 0x6000); + dmp_reg = ®->iobase_window; + reg_data = RD_REG_DWORD(dmp_reg); + WRT_REG_DWORD(dmp_reg, 0); + + dmp_reg = ®->unused_4_1[0]; + reg_data = RD_REG_DWORD(dmp_reg); + WRT_REG_DWORD(dmp_reg, 0); + + WRT_REG_DWORD(®->iobase_addr, 0x6010); + dmp_reg = ®->unused_4_1[2]; + reg_data = RD_REG_DWORD(dmp_reg); + WRT_REG_DWORD(dmp_reg, 0); + + // select PCR and disable ecc checking and correction + WRT_REG_DWORD(®->iobase_addr, 0x0F70); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_select, 0x60000000); // write to F0h = PCR + + /* Host/Risc registers. */ + iter_reg = fw->host_risc_reg; + iter_reg = qla24xx_read_window(reg, 0x7000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x7010, 16, iter_reg); + qla24xx_read_window(reg, 0x7040, 16, iter_reg); + + /* PCIe registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x7C00); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_window, 0x01); + dmp_reg = ®->iobase_c4; + fw->pcie_regs[0] = htonl(RD_REG_DWORD(dmp_reg++)); + fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++)); + fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg)); + fw->pcie_regs[3] = htonl(RD_REG_DWORD(®->iobase_window)); + + WRT_REG_DWORD(®->iobase_window, 0x00); + RD_REG_DWORD(®->iobase_window); + + /* Host interface registers. */ + dmp_reg = ®->flash_addr; + for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++) + fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Disable interrupts. */ + WRT_REG_DWORD(®->ictrl, 0); + RD_REG_DWORD(®->ictrl); + + /* Shadow registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x0F70); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_select, 0xB0000000); + fw->shadow_reg[0] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0100000); + fw->shadow_reg[1] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0200000); + fw->shadow_reg[2] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0300000); + fw->shadow_reg[3] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0400000); + fw->shadow_reg[4] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0500000); + fw->shadow_reg[5] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0600000); + fw->shadow_reg[6] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0700000); + fw->shadow_reg[7] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0800000); + fw->shadow_reg[8] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0900000); + fw->shadow_reg[9] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0A00000); + fw->shadow_reg[10] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + /* RISC I/O register. */ + WRT_REG_DWORD(®->iobase_addr, 0x0010); + fw->risc_io_reg = htonl(RD_REG_DWORD(®->iobase_window)); + + /* Mailbox registers. */ + mbx_reg = ®->mailbox0; + for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++) + fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++)); + + /* Transfer sequence registers. */ + iter_reg = fw->xseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xBE00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBE10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBE20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBE30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBE40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBE50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBE60, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBE70, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg); + qla24xx_read_window(reg, 0xBF70, 16, iter_reg); + + iter_reg = fw->xseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xBFC0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBFD0, 16, iter_reg); + qla24xx_read_window(reg, 0xBFE0, 16, iter_reg); + + qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg); + + qla24xx_read_window(reg, 0xBEF0, 16, fw->xseq_2_reg); + + /* Receive sequence registers. */ + iter_reg = fw->rseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xFE00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFE10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFE20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFE30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFE40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFE50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFE60, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFE70, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg); + qla24xx_read_window(reg, 0xFF70, 16, iter_reg); + + iter_reg = fw->rseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xFFC0, 16, iter_reg); + qla24xx_read_window(reg, 0xFFD0, 16, iter_reg); + + qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg); + qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg); + qla24xx_read_window(reg, 0xFEF0, 16, fw->rseq_3_reg); + + /* Auxiliary sequence registers. */ + iter_reg = fw->aseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xB000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB060, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB070, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB100, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB110, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB120, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB130, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB140, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB150, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB160, 16, iter_reg); + qla24xx_read_window(reg, 0xB170, 16, iter_reg); + + iter_reg = fw->aseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xB0C0, 16, iter_reg); + qla24xx_read_window(reg, 0xB0D0, 16, iter_reg); + + qla24xx_read_window(reg, 0xB0E0, 16, fw->aseq_1_reg); + qla24xx_read_window(reg, 0xB0F0, 16, fw->aseq_2_reg); + qla24xx_read_window(reg, 0xB1F0, 16, fw->aseq_3_reg); + + /* Command DMA registers. */ + iter_reg = fw->cmd_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7100, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x7120, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x7130, 16, iter_reg); + qla24xx_read_window(reg, 0x71F0, 16, iter_reg); + + /* Queues. */ + iter_reg = fw->req0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->resp0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->req1_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Transmit DMA registers. */ + iter_reg = fw->xmt0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg); + qla24xx_read_window(reg, 0x7610, 16, iter_reg); + + iter_reg = fw->xmt1_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg); + qla24xx_read_window(reg, 0x7630, 16, iter_reg); + + iter_reg = fw->xmt2_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg); + qla24xx_read_window(reg, 0x7650, 16, iter_reg); + + iter_reg = fw->xmt3_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg); + qla24xx_read_window(reg, 0x7670, 16, iter_reg); + + iter_reg = fw->xmt4_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg); + qla24xx_read_window(reg, 0x7690, 16, iter_reg); + + qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg); + + /* Receive DMA registers. */ + iter_reg = fw->rcvt0_data_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg); + qla24xx_read_window(reg, 0x7710, 16, iter_reg); + + iter_reg = fw->rcvt1_data_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg); + qla24xx_read_window(reg, 0x7730, 16, iter_reg); + + /* RISC registers. */ + iter_reg = fw->risc_gp_reg; + iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg); + qla24xx_read_window(reg, 0x0F70, 16, iter_reg); + + /* Local memory controller registers. */ + iter_reg = fw->lmc_reg; + iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3060, 16, iter_reg); + qla24xx_read_window(reg, 0x3070, 16, iter_reg); + + /* Fibre Protocol Module registers. */ + iter_reg = fw->fpm_hdw_reg; + iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40C0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40D0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40E0, 16, iter_reg); + qla24xx_read_window(reg, 0x40F0, 16, iter_reg); + + /* RQ0 Array registers. */ + iter_reg = fw->rq0_array_reg; + iter_reg = qla24xx_read_window(reg, 0x5C00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C60, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C70, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C80, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5C90, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5CA0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5CB0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5CC0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5CD0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5CE0, 16, iter_reg); + qla24xx_read_window(reg, 0x5CF0, 16, iter_reg); + + /* RQ1 Array registers. */ + iter_reg = fw->rq1_array_reg; + iter_reg = qla24xx_read_window(reg, 0x5D00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D60, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D70, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D80, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5D90, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5DA0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5DB0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5DC0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5DD0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5DE0, 16, iter_reg); + qla24xx_read_window(reg, 0x5DF0, 16, iter_reg); + + /* RP0 Array registers. */ + iter_reg = fw->rp0_array_reg; + iter_reg = qla24xx_read_window(reg, 0x5E00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E60, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E70, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E80, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5E90, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5EA0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5EB0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5EC0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5ED0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5EE0, 16, iter_reg); + qla24xx_read_window(reg, 0x5EF0, 16, iter_reg); + + /* RP1 Array registers. */ + iter_reg = fw->rp1_array_reg; + iter_reg = qla24xx_read_window(reg, 0x5F00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F60, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F70, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F80, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5F90, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5FA0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5FB0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5FC0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5FD0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x5FE0, 16, iter_reg); + qla24xx_read_window(reg, 0x5FF0, 16, iter_reg); + + iter_reg = fw->at0_array_reg; + iter_reg = qla24xx_read_window(reg, 0x7080, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x7090, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x70A0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x70B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x70C0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x70D0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x70E0, 16, iter_reg); + qla24xx_read_window(reg, 0x70F0, 16, iter_reg); + + /* I/O Queue Control registers. */ + qla24xx_read_window(reg, 0x7800, 16, fw->queue_control_reg); + + /* Frame Buffer registers. */ + iter_reg = fw->fb_hdw_reg; + iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6060, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6070, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x61C0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6530, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6540, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6550, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6560, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6570, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6580, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6590, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x65A0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x65B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x65C0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x65D0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x65E0, 16, iter_reg); + qla24xx_read_window(reg, 0x6F00, 16, iter_reg); + + /* Multi queue registers */ + nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset, + &last_chain); + + rval = qla24xx_soft_reset(ha); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xd00e, + "SOFT RESET FAILED, forcing continuation of dump!!!\n"); + rval = QLA_SUCCESS; + + ql_log(ql_log_warn, vha, 0xd00f, "try a bigger hammer!!!\n"); + + WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET); + RD_REG_DWORD(®->hccr); + + WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); + RD_REG_DWORD(®->hccr); + + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_RESET); + RD_REG_DWORD(®->hccr); + + for (cnt = 30000; cnt && (RD_REG_WORD(®->mailbox0)); cnt--) { + udelay(5); + } + + if (!cnt) { + nxt = fw->code_ram; + nxt += sizeof(fw->code_ram), + nxt += (ha->fw_memory_size - 0x100000 + 1); + goto copy_queue; + } else + ql_log(ql_log_warn, vha, 0xd010, + "bigger hammer success?\n"); + } + + rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram), + &nxt); + if (rval != QLA_SUCCESS) + goto qla83xx_fw_dump_failed_0; + +copy_queue: + nxt = qla2xxx_copy_queues(ha, nxt); + + qla24xx_copy_eft(ha, nxt); + + /* Chain entries -- started with MQ. */ + nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain); + nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); + if (last_chain) { + ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); + *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + } + + /* Adjust valid length. */ + ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump); + +qla83xx_fw_dump_failed_0: + qla2xxx_dump_post_process(base_vha, rval); + +qla83xx_fw_dump_failed: + if (!hardware_locked) + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + /****************************************************************************/ /* Driver Debug Functions. */ /****************************************************************************/ +static inline int +ql_mask_match(uint32_t level) +{ + if (ql2xextended_error_logging == 1) + ql2xextended_error_logging = QL_DBG_DEFAULT1_MASK; + return (level & ql2xextended_error_logging) == level; +} + +/* + * This function is for formatting and logging debug information. + * It is to be used when vha is available. It formats the message + * and logs it to the messages file. + * parameters: + * level: The level of the debug messages to be printed. + * If ql2xextended_error_logging value is correctly set, + * this message will appear in the messages file. + * vha: Pointer to the scsi_qla_host_t. + * id: This is a unique identifier for the level. It identifies the + * part of the code from where the message originated. + * msg: The message to be displayed. + */ void -qla2x00_dump_regs(scsi_qla_host_t *ha) +ql_dbg(uint32_t level, scsi_qla_host_t *vha, int32_t id, const char *fmt, ...) +{ + va_list va; + + if (!ql_mask_match(level)) + return; + + va_start(va, fmt); + + if (vha != NULL) { + const struct pci_dev *pdev = vha->hw->pdev; + /* : Message */ + pr_warning("%s [%s]-%04x:%ld: ", + QL_MSGHDR, dev_name(&(pdev->dev)), id + ql_dbg_offset, + vha->host_no); + } else { + pr_warning("%s [%s]-%04x: ", + QL_MSGHDR, "0000:00:00.0", id + ql_dbg_offset); + } + + vprintk(fmt, va); + va_end(va); + +} + +/* + * This function is for formatting and logging debug information. + * It is to be used when vha is not available and pci is availble, + * i.e., before host allocation. It formats the message and logs it + * to the messages file. + * parameters: + * level: The level of the debug messages to be printed. + * If ql2xextended_error_logging value is correctly set, + * this message will appear in the messages file. + * pdev: Pointer to the struct pci_dev. + * id: This is a unique id for the level. It identifies the part + * of the code from where the message originated. + * msg: The message to be displayed. + */ +void +ql_dbg_pci(uint32_t level, struct pci_dev *pdev, int32_t id, + const char *fmt, ...) +{ + va_list va; + + if (pdev == NULL) + return; + if (!ql_mask_match(level)) + return; + + va_start(va, fmt); + + /* : Message */ + pr_warning("%s [%s]-%04x: ", + QL_MSGHDR, dev_name(&(pdev->dev)), id + ql_dbg_offset); + + vprintk(fmt, va); + va_end(va); +} + +/* + * This function is for formatting and logging log messages. + * It is to be used when vha is available. It formats the message + * and logs it to the messages file. All the messages will be logged + * irrespective of value of ql2xextended_error_logging. + * parameters: + * level: The level of the log messages to be printed in the + * messages file. + * vha: Pointer to the scsi_qla_host_t + * id: This is a unique id for the level. It identifies the + * part of the code from where the message originated. + * msg: The message to be displayed. + */ +void +ql_log(uint32_t level, scsi_qla_host_t *vha, int32_t id, const char *fmt, ...) +{ + va_list va; + char pbuf[128]; + + if (level > ql_errlev) + return; + + if (vha != NULL) { + const struct pci_dev *pdev = vha->hw->pdev; + /* : Message */ + snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x:%ld: ", + QL_MSGHDR, dev_name(&(pdev->dev)), id, vha->host_no); + } else { + snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x: ", + QL_MSGHDR, "0000:00:00.0", id); + } + pbuf[sizeof(pbuf) - 1] = 0; + + va_start(va, fmt); + + switch (level) { + case ql_log_fatal: /* FATAL LOG */ + pr_crit("%s", pbuf); + break; + case ql_log_warn: + pr_err("%s", pbuf); + break; + case ql_log_info: + pr_warning("%s", pbuf); + break; + default: + pr_info("%s", pbuf); + break; + } + + vprintk(fmt, va); + va_end(va); +} + +/* + * This function is for formatting and logging log messages. + * It is to be used when vha is not available and pci is availble, + * i.e., before host allocation. It formats the message and logs + * it to the messages file. All the messages are logged irrespective + * of the value of ql2xextended_error_logging. + * parameters: + * level: The level of the log messages to be printed in the + * messages file. + * pdev: Pointer to the struct pci_dev. + * id: This is a unique id for the level. It identifies the + * part of the code from where the message originated. + * msg: The message to be displayed. + */ +void +ql_log_pci(uint32_t level, struct pci_dev *pdev, int32_t id, + const char *fmt, ...) +{ + va_list va; + char pbuf[128]; + + if (level > ql_errlev) + return; + + /* : Message */ + snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x: ", + QL_MSGHDR, dev_name(&(pdev->dev)), id); + pbuf[sizeof(pbuf) - 1] = 0; + + va_start(va, fmt); + + switch (level) { + case ql_log_fatal: /* FATAL LOG */ + pr_crit("%s", pbuf); + break; + case ql_log_warn: + pr_err("%s", pbuf); + break; + case ql_log_info: + pr_warning("%s", pbuf); + break; + default: + pr_info("%s", pbuf); + break; + } + + vprintk(fmt, va); + va_end(va); +} + +void +ql_dump_regs(uint32_t level, scsi_qla_host_t *vha, int32_t id) { int i; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; + struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82; uint16_t __iomem *mbx_reg; - mbx_reg = IS_FWI2_CAPABLE(ha) ? ®24->mailbox0: - MAILBOX_REG(ha, reg, 0); + if (!ql_mask_match(level)) + return; - printk("Mailbox registers:\n"); + if (IS_QLA82XX(ha)) + mbx_reg = ®82->mailbox_in[0]; + else if (IS_FWI2_CAPABLE(ha)) + mbx_reg = ®24->mailbox0; + else + mbx_reg = MAILBOX_REG(ha, reg, 0); + + ql_dbg(level, vha, id, "Mailbox registers:\n"); for (i = 0; i < 6; i++) - printk("scsi(%ld): mbox %d 0x%04x \n", ha->host_no, i, - RD_REG_WORD(mbx_reg++)); + ql_dbg(level, vha, id, + "mbox[%d] 0x%04x\n", i, RD_REG_WORD(mbx_reg++)); } void -qla2x00_dump_buffer(uint8_t * b, uint32_t size) +ql_dump_buffer(uint32_t level, scsi_qla_host_t *vha, int32_t id, + uint8_t *b, uint32_t size) { uint32_t cnt; uint8_t c; - printk(" 0 1 2 3 4 5 6 7 8 9 " - "Ah Bh Ch Dh Eh Fh\n"); - printk("----------------------------------------" - "----------------------\n"); + if (!ql_mask_match(level)) + return; + ql_dbg(level, vha, id, " 0 1 2 3 4 5 6 7 8 " + "9 Ah Bh Ch Dh Eh Fh\n"); + ql_dbg(level, vha, id, "----------------------------------" + "----------------------------\n"); + + ql_dbg(level, vha, id, " "); for (cnt = 0; cnt < size;) { c = *b++; - printk("%02x",(uint32_t) c); + printk("%02x", (uint32_t) c); cnt++; if (!(cnt % 16)) printk("\n"); @@ -1300,5 +2482,5 @@ qla2x00_dump_buffer(uint8_t * b, uint32_t size) printk(" "); } if (cnt % 16) - printk("\n"); + ql_dbg(level, vha, id, "\n"); } diff --git a/qla2x00t/qla_dbg.h b/qla2x00t/qla_dbg.h index 2e9c0c097..f031ec045 100644 --- a/qla2x00t/qla_dbg.h +++ b/qla2x00t/qla_dbg.h @@ -1,132 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ -/* - * Driver debug definitions. - */ -/* #define QL_DEBUG_LEVEL_1 */ /* Output register accesses to COM1 */ -/* #define QL_DEBUG_LEVEL_2 */ /* Output error msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_3 */ /* Output function trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_4 */ /* Output NVRAM trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_5 */ /* Output ring trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_6 */ /* Output WATCHDOG timer trace to COM1 */ -/* #define QL_DEBUG_LEVEL_7 */ /* Output RISC load trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_8 */ /* Output ring saturation msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_9 */ /* Output IOCTL trace msgs */ -/* #define QL_DEBUG_LEVEL_10 */ /* Output IOCTL error msgs */ -/* #define QL_DEBUG_LEVEL_11 */ /* Output Mbx Cmd trace msgs */ -/* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */ -/* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */ -/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */ -/* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */ -/* #define QL_DEBUG_LEVEL_16 */ /* Output ISP84XX trace msgs */ -/* -* Macros use for debugging the driver. -*/ - -#define DEBUG(x) do { if (ql2xextended_error_logging) { x; } } while (0) - -#if defined(QL_DEBUG_LEVEL_1) -#define DEBUG1(x) do {x;} while (0) -#else -#define DEBUG1(x) do {} while (0) -#endif - -#define DEBUG2(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_3(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_3_11(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_9_10(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_11(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_13(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_16(x) do { if (ql2xextended_error_logging) { x; } } while (0) - -#if defined(QL_DEBUG_LEVEL_3) -#define DEBUG3(x) do {x;} while (0) -#define DEBUG3_11(x) do {x;} while (0) -#else -#define DEBUG3(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_4) -#define DEBUG4(x) do {x;} while (0) -#else -#define DEBUG4(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_5) -#define DEBUG5(x) do {x;} while (0) -#else -#define DEBUG5(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_7) -#define DEBUG7(x) do {x;} while (0) -#else -#define DEBUG7(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_9) -#define DEBUG9(x) do {x;} while (0) -#define DEBUG9_10(x) do {x;} while (0) -#else -#define DEBUG9(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_10) -#define DEBUG10(x) do {x;} while (0) -#define DEBUG9_10(x) do {x;} while (0) -#else -#define DEBUG10(x) do {} while (0) - #if !defined(DEBUG9_10) - #define DEBUG9_10(x) do {} while (0) - #endif -#endif - -#if defined(QL_DEBUG_LEVEL_11) -#define DEBUG11(x) do{x;} while(0) -#if !defined(DEBUG3_11) -#define DEBUG3_11(x) do{x;} while(0) -#endif -#else -#define DEBUG11(x) do{} while(0) - #if !defined(QL_DEBUG_LEVEL_3) - #define DEBUG3_11(x) do{} while(0) - #endif -#endif - -#if defined(QL_DEBUG_LEVEL_12) -#define DEBUG12(x) do {x;} while (0) -#else -#define DEBUG12(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_13) -#define DEBUG13(x) do {x;} while (0) -#else -#define DEBUG13(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_14) -#define DEBUG14(x) do {x;} while (0) -#else -#define DEBUG14(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_15) -#define DEBUG15(x) do {x;} while (0) -#else -#define DEBUG15(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_16) -#define DEBUG16(x) do {x;} while (0) -#else -#define DEBUG16(x) do {} while (0) -#endif +#include "qla_def.h" /* * Firmware Dump structure definition @@ -247,6 +126,93 @@ struct qla25xx_fw_dump { uint32_t ext_mem[1]; }; +struct qla81xx_fw_dump { + uint32_t host_status; + uint32_t host_risc_reg[32]; + uint32_t pcie_regs[4]; + uint32_t host_reg[32]; + uint32_t shadow_reg[11]; + uint32_t risc_io_reg; + uint16_t mailbox_reg[32]; + uint32_t xseq_gp_reg[128]; + uint32_t xseq_0_reg[48]; + uint32_t xseq_1_reg[16]; + uint32_t rseq_gp_reg[128]; + uint32_t rseq_0_reg[32]; + uint32_t rseq_1_reg[16]; + uint32_t rseq_2_reg[16]; + uint32_t aseq_gp_reg[128]; + uint32_t aseq_0_reg[32]; + uint32_t aseq_1_reg[16]; + uint32_t aseq_2_reg[16]; + uint32_t cmd_dma_reg[16]; + uint32_t req0_dma_reg[15]; + uint32_t resp0_dma_reg[15]; + uint32_t req1_dma_reg[15]; + uint32_t xmt0_dma_reg[32]; + uint32_t xmt1_dma_reg[32]; + uint32_t xmt2_dma_reg[32]; + uint32_t xmt3_dma_reg[32]; + uint32_t xmt4_dma_reg[32]; + uint32_t xmt_data_dma_reg[16]; + uint32_t rcvt0_data_dma_reg[32]; + uint32_t rcvt1_data_dma_reg[32]; + uint32_t risc_gp_reg[128]; + uint32_t lmc_reg[128]; + uint32_t fpm_hdw_reg[224]; + uint32_t fb_hdw_reg[208]; + uint32_t code_ram[0x2000]; + uint32_t ext_mem[1]; +}; + +struct qla83xx_fw_dump { + uint32_t host_status; + uint32_t host_risc_reg[48]; + uint32_t pcie_regs[4]; + uint32_t host_reg[32]; + uint32_t shadow_reg[11]; + uint32_t risc_io_reg; + uint16_t mailbox_reg[32]; + uint32_t xseq_gp_reg[256]; + uint32_t xseq_0_reg[48]; + uint32_t xseq_1_reg[16]; + uint32_t xseq_2_reg[16]; + uint32_t rseq_gp_reg[256]; + uint32_t rseq_0_reg[32]; + uint32_t rseq_1_reg[16]; + uint32_t rseq_2_reg[16]; + uint32_t rseq_3_reg[16]; + uint32_t aseq_gp_reg[256]; + uint32_t aseq_0_reg[32]; + uint32_t aseq_1_reg[16]; + uint32_t aseq_2_reg[16]; + uint32_t aseq_3_reg[16]; + uint32_t cmd_dma_reg[64]; + uint32_t req0_dma_reg[15]; + uint32_t resp0_dma_reg[15]; + uint32_t req1_dma_reg[15]; + uint32_t xmt0_dma_reg[32]; + uint32_t xmt1_dma_reg[32]; + uint32_t xmt2_dma_reg[32]; + uint32_t xmt3_dma_reg[32]; + uint32_t xmt4_dma_reg[32]; + uint32_t xmt_data_dma_reg[16]; + uint32_t rcvt0_data_dma_reg[32]; + uint32_t rcvt1_data_dma_reg[32]; + uint32_t risc_gp_reg[128]; + uint32_t lmc_reg[128]; + uint32_t fpm_hdw_reg[256]; + uint32_t rq0_array_reg[256]; + uint32_t rq1_array_reg[256]; + uint32_t rp0_array_reg[256]; + uint32_t rp1_array_reg[256]; + uint32_t queue_control_reg[16]; + uint32_t fb_hdw_reg[432]; + uint32_t at0_array_reg[128]; + uint32_t code_ram[0x2400]; + uint32_t ext_mem[1]; +}; + #define EFT_NUM_BUFFERS 4 #define EFT_BYTES_PER_BUFFER 0x4000 #define EFT_SIZE ((EFT_BYTES_PER_BUFFER) * (EFT_NUM_BUFFERS)) @@ -266,8 +232,31 @@ struct qla2xxx_fce_chain { uint32_t eregs[8]; }; +struct qla2xxx_mq_chain { + uint32_t type; + uint32_t chain_size; + + uint32_t count; + uint32_t qregs[4 * QLA_MQ_SIZE]; +}; + +struct qla2xxx_mqueue_header { + uint32_t queue; +#define TYPE_REQUEST_QUEUE 0x1 +#define TYPE_RESPONSE_QUEUE 0x2 + uint32_t number; + uint32_t size; +}; + +struct qla2xxx_mqueue_chain { + uint32_t type; + uint32_t chain_size; +}; + #define DUMP_CHAIN_VARIANT 0x80000000 #define DUMP_CHAIN_FCE 0x7FFFFAF0 +#define DUMP_CHAIN_MQ 0x7FFFFAF1 +#define DUMP_CHAIN_QUEUE 0x7FFFFAF2 #define DUMP_CHAIN_LAST 0x80000000 struct qla2xxx_fw_dump { @@ -300,5 +289,62 @@ struct qla2xxx_fw_dump { struct qla2300_fw_dump isp23; struct qla24xx_fw_dump isp24; struct qla25xx_fw_dump isp25; + struct qla81xx_fw_dump isp81; + struct qla83xx_fw_dump isp83; } isp; }; + +#define QL_MSGHDR "qla2xxx" +#define QL_DBG_DEFAULT1_MASK 0x1e400000 + +#define ql_log_fatal 0 /* display fatal errors */ +#define ql_log_warn 1 /* display critical errors */ +#define ql_log_info 2 /* display all recovered errors */ +#define ql_log_all 3 /* This value is only used by ql_errlev. + * No messages will use this value. + * This should be always highest value + * as compared to other log levels. + */ + +extern int ql_errlev; + +void __attribute__((format (printf, 4, 5))) +ql_dbg(uint32_t, scsi_qla_host_t *vha, int32_t, const char *fmt, ...); +void __attribute__((format (printf, 4, 5))) +ql_dbg_pci(uint32_t, struct pci_dev *pdev, int32_t, const char *fmt, ...); + +void __attribute__((format (printf, 4, 5))) +ql_log(uint32_t, scsi_qla_host_t *vha, int32_t, const char *fmt, ...); +void __attribute__((format (printf, 4, 5))) +ql_log_pci(uint32_t, struct pci_dev *pdev, int32_t, const char *fmt, ...); + +/* Debug Levels */ +/* The 0x40000000 is the max value any debug level can have + * as ql2xextended_error_logging is of type signed int + */ +#define ql_dbg_init 0x40000000 /* Init Debug */ +#define ql_dbg_mbx 0x20000000 /* MBX Debug */ +#define ql_dbg_disc 0x10000000 /* Device Discovery Debug */ +#define ql_dbg_io 0x08000000 /* IO Tracing Debug */ +#define ql_dbg_dpc 0x04000000 /* DPC Thead Debug */ +#define ql_dbg_async 0x02000000 /* Async events Debug */ +#define ql_dbg_timer 0x01000000 /* Timer Debug */ +#define ql_dbg_user 0x00800000 /* User Space Interations Debug */ +#define ql_dbg_taskm 0x00400000 /* Task Management Debug */ +#define ql_dbg_aer 0x00200000 /* AER/EEH Debug */ +#define ql_dbg_multiq 0x00100000 /* MultiQ Debug */ +#define ql_dbg_p3p 0x00080000 /* P3P specific Debug */ +#define ql_dbg_vport 0x00040000 /* Virtual Port Debug */ +#define ql_dbg_buffer 0x00020000 /* For dumping the buffer/regs */ +#define ql_dbg_misc 0x00010000 /* For dumping everything that is not + * not covered by upper categories + */ +#define ql_dbg_verbose 0x00008000 /* More verbosity for each level + * This is to be used with other levels where + * more verbosity is required. It might not + * be applicable to all the levels. + */ + +#define ql_dbg_tgt 0x00004000 /* Target mode */ +#define ql_dbg_tgt_mgt 0x00002000 /* Target mode management */ +#define ql_dbg_tgt_tmr 0x00001000 /* Target mode task management */ diff --git a/qla2x00t/qla_def.h b/qla2x00t/qla_def.h index 53246d453..3f2dcb32d 100644 --- a/qla2x00t/qla_def.h +++ b/qla2x00t/qla_def.h @@ -1,12 +1,13 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #ifndef __QLA_DEF_H #define __QLA_DEF_H +#include #include #include #include @@ -25,15 +26,25 @@ #include #include #include -#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) +#error +#error ***This version of qla2xxx does not support distributions based on*** +#error ***kernels less than 2.6.32.*** +#error +#endif #include #include #include #include #include +#include -#define QLA2XXX_DRIVER_NAME "qla2xxx" +#include "qla_bsg.h" +#include "qla_nx.h" +#define QLA2XXX_DRIVER_NAME "qla2xxx" +#define QLA2XXX_APIDEV "ql2xapidev" /* * We have MAILBOX_REGISTER_COUNT sized arrays in a few places, @@ -41,6 +52,7 @@ * ISP2100 HBAs. */ #define MAILBOX_REGISTER_COUNT_2100 8 +#define MAILBOX_REGISTER_COUNT_2200 24 #define MAILBOX_REGISTER_COUNT 32 #define QLA2200A_RISC_ROM_VER 4 @@ -94,67 +106,7 @@ #define LSD(x) ((uint32_t)((uint64_t)(x))) #define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16)) -/* CT IU */ -typedef struct{ - uint8_t revision; - uint8_t in_id[3]; - uint8_t gs_type; - uint8_t gs_subtype; - uint8_t options; - uint8_t reserved0; - uint16_t command; - uint16_t max_rsp_size; - uint8_t fragment_id; - uint8_t reserved1[3]; -} ct_iu_t; - -/* CT request format */ -typedef struct { - ct_iu_t ct_iu; - union { - struct { - uint8_t reserved; - uint8_t port_id[3]; - } port_id; - - struct { - uint8_t port_type; - uint8_t domain; - uint8_t area; - uint8_t reserved; - } gid_pt; - - struct { - uint8_t reserved; - uint8_t port_id[3]; - uint8_t fc4_types[32]; - } rft_id; - - struct { - uint8_t reserved; - uint8_t port_id[3]; - uint16_t reserved2; - uint8_t fc4_feature; - uint8_t fc4_type; - } rff_id; - - struct { - uint8_t reserved; - uint8_t port_id[3]; - uint8_t node_name[8]; - } rnn_id; - - struct { - uint8_t node_name[8]; - uint8_t name_len; - uint8_t sym_node_name[255]; - } rsnn_nn; - - struct { - uint8_t hba_indentifier[8]; - } ghat; - } extended; -} fc_ct_request_t; +#define MAKE_HANDLE(x, y) ((uint32_t)((((uint32_t)(x)) << 16) | (uint32_t)(y))) /* * I/O register @@ -181,17 +133,17 @@ typedef struct { * Fibre Channel device definitions. */ #define WWN_SIZE 8 /* Size of WWPN, WWN & WWNN */ -#define MAX_FIBRE_DEVICES 512 +#define MAX_FIBRE_DEVICES_2100 512 +#define MAX_FIBRE_DEVICES_2400 2048 +#define MAX_FIBRE_DEVICES_LOOP 128 +#define MAX_FIBRE_DEVICES_MAX MAX_FIBRE_DEVICES_2400 #define MAX_FIBRE_LUNS 0xFFFF -#define MAX_RSCN_COUNT 32 #define MAX_HOST_COUNT 16 /* * Host adapter default definitions. */ #define MAX_BUSES 1 /* We only have one bus today */ -#define MAX_TARGETS_2100 MAX_FIBRE_DEVICES -#define MAX_TARGETS_2200 MAX_FIBRE_DEVICES #define MIN_LUNS 8 #define MAX_LUNS MAX_FIBRE_LUNS #define MAX_CMDS_PER_LUN 255 @@ -233,49 +185,138 @@ typedef struct { #define LOOP_DOWN_RESET (LOOP_DOWN_TIME - 30) /* Maximum outstanding commands in ISP queues (1-65535) */ +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +#define MAX_OUTSTANDING_COMMANDS 4096 +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ #define MAX_OUTSTANDING_COMMANDS 1024 +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ /* ISP request and response entry counts (37-65535) */ #define REQUEST_ENTRY_CNT_2100 128 /* Number of request entries. */ #define REQUEST_ENTRY_CNT_2200 2048 /* Number of request entries. */ -#define REQUEST_ENTRY_CNT_2XXX_EXT_MEM 4096 /* Number of request entries. */ -#define REQUEST_ENTRY_CNT_24XX 4096 /* Number of request entries. */ #define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/ #define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/ #define ATIO_ENTRY_CNT_24XX 4096 /* Number of ATIO entries. */ +#define RESPONSE_ENTRY_CNT_MQ 128 /* Number of response entries.*/ + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +#define REQUEST_ENTRY_CNT_24XX 4096 /* Number of request entries. */ +#define RESPONSE_ENTRY_CNT_24XX 4096 /* Number of response entries.*/ +#else +#define REQUEST_ENTRY_CNT_24XX 2048 /* Number of request entries. */ +#define RESPONSE_ENTRY_CNT_24XX RESPONSE_ENTRY_CNT_2300 +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + +struct req_que; + +/* + * (sd.h is not exported, hence local inclusion) + * Data Integrity Field tuple. + */ +struct sd_dif_tuple { + __be16 guard_tag; /* Checksum */ + __be16 app_tag; /* Opaque storage */ + __be32 ref_tag; /* Target LBA or indirect LBA */ +}; /* * SCSI Request Block */ -typedef struct srb { - struct scsi_qla_host *ha; /* HA the SP is queued on */ - struct fc_port *fcport; - +struct srb_cmd { struct scsi_cmnd *cmd; /* Linux SCSI command pkt */ - - uint16_t flags; - uint32_t request_sense_length; uint8_t *request_sense_ptr; -} srb_t; + void *ctx; +}; /* * SRB flag definitions */ -#define SRB_TIMEOUT BIT_0 /* Command timed out */ -#define SRB_DMA_VALID BIT_1 /* Command sent to ISP */ -#define SRB_WATCHDOG BIT_2 /* Command on watchdog list */ -#define SRB_ABORT_PENDING BIT_3 /* Command abort sent to device */ +#define SRB_DMA_VALID BIT_0 /* Command sent to ISP */ +#define SRB_FCP_CMND_DMA_VALID BIT_12 /* DIF: DSD List valid */ +#define SRB_CRC_CTX_DMA_VALID BIT_2 /* DIF: context DMA valid */ +#define SRB_CRC_PROT_DMA_VALID BIT_4 /* DIF: prot DMA valid */ +#define SRB_CRC_CTX_DSD_VALID BIT_5 /* DIF: dsd_list valid */ -#define SRB_ABORTED BIT_4 /* Command aborted command already */ -#define SRB_RETRY BIT_5 /* Command needs retrying */ -#define SRB_GOT_SENSE BIT_6 /* Command has sense data */ -#define SRB_FAILOVER BIT_7 /* Command in failover state */ +/* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */ +#define IS_PROT_IO(sp) (sp->flags & SRB_CRC_CTX_DSD_VALID) -#define SRB_BUSY BIT_8 /* Command is in busy retry state */ -#define SRB_FO_CANCEL BIT_9 /* Command don't need to do failover */ -#define SRB_IOCTL BIT_10 /* IOCTL command. */ -#define SRB_TAPE BIT_11 /* FCP2 (Tape) command. */ +/* + * SRB extensions. + */ +struct srb_iocb { + union { + struct { + uint16_t flags; +#define SRB_LOGIN_RETRIED BIT_0 +#define SRB_LOGIN_COND_PLOGI BIT_1 +#define SRB_LOGIN_SKIP_PRLI BIT_2 + uint16_t data[2]; + } logio; + struct { + /* + * Values for flags field below are as + * defined in tsk_mgmt_entry struct + * for control_flags field in qla_fw.h. + */ + uint32_t flags; + uint32_t lun; + uint32_t data; + } tmf; + } u; + + struct timer_list timer; + void (*timeout)(void *); +}; + +/* Values for srb_ctx type */ +#define SRB_LOGIN_CMD 1 +#define SRB_LOGOUT_CMD 2 +#define SRB_ELS_CMD_RPT 3 +#define SRB_ELS_CMD_HST 4 +#define SRB_CT_CMD 5 +#define SRB_ADISC_CMD 6 +#define SRB_TM_CMD 7 +#define SRB_SCSI_CMD 8 + +typedef struct srb { + atomic_t ref_count; + struct fc_port *fcport; + uint32_t handle; + uint16_t flags; + uint16_t type; + char *name; + int iocbs; + union { + struct srb_iocb iocb_cmd; + struct fc_bsg_job *bsg_job; + struct srb_cmd scmd; + } u; + void (*done)(void *, void *, int); + void (*free)(void *, void *); +} srb_t; + +#define GET_CMD_SP(sp) (sp->u.scmd.cmd) +#define SET_CMD_SP(sp, cmd) (sp->u.scmd.cmd = cmd) +#define GET_CMD_CTX_SP(sp) (sp->u.scmd.ctx) + +#define GET_CMD_SENSE_LEN(sp) \ + (sp->u.scmd.request_sense_length) +#define SET_CMD_SENSE_LEN(sp, len) \ + (sp->u.scmd.request_sense_length = len) +#define GET_CMD_SENSE_PTR(sp) \ + (sp->u.scmd.request_sense_ptr) +#define SET_CMD_SENSE_PTR(sp, ptr) \ + (sp->u.scmd.request_sense_ptr = ptr) + +struct msg_echo_lb { + dma_addr_t send_dma; + dma_addr_t rcv_dma; + uint16_t req_sg_cnt; + uint16_t rsp_sg_cnt; + uint16_t options; + uint32_t transfer_size; +}; /* * ISP I/O Register Set structure definitions. @@ -432,9 +473,20 @@ struct device_reg_2xxx { } u_end; }; +struct device_reg_25xxmq { + uint32_t req_q_in; + uint32_t req_q_out; + uint32_t rsp_q_in; + uint32_t rsp_q_out; + uint32_t atio_q_in; + uint32_t atio_q_out; +}; + typedef union { struct device_reg_2xxx isp; struct device_reg_24xx isp24; + struct device_reg_25xxmq isp25mq; + struct device_reg_82xx isp82; } device_reg_t; #define ISP_REQ_Q_IN(ha, reg) \ @@ -454,6 +506,9 @@ typedef union { &(reg)->u.isp2100.mailbox5 : \ &(reg)->u.isp2300.rsp_q_out) +#define ISP_ATIO_Q_IN(vha) (vha->hw->atio_q_in) +#define ISP_ATIO_Q_OUT(vha) (vha->hw->atio_q_out) + #define MAILBOX_REG(ha, reg, num) \ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \ (num < 8 ? \ @@ -528,6 +583,7 @@ typedef struct { #define MBA_SYSTEM_ERR 0x8002 /* System Error. */ #define MBA_REQ_TRANSFER_ERR 0x8003 /* Request Transfer Error. */ #define MBA_RSP_TRANSFER_ERR 0x8004 /* Response Transfer Error. */ +#define MBA_WAKEUP_THRES 0x8005 /* Request Queue Wake-up. */ #define MBA_ATIO_TRANSFER_ERR 0x8005 /* ATIO Queue Transfer Error. */ #define MBA_LIP_OCCURRED 0x8010 /* Loop Initialization Procedure */ /* occurred. */ @@ -563,6 +619,8 @@ typedef struct { #define MBA_DISCARD_RND_FRAME 0x8048 /* discard RND frame due to error. */ #define MBA_REJECTED_FCP_CMD 0x8049 /* rejected FCP_CMD. */ +/* ISP mailbox loopback echo diagnostic error code */ +#define MBS_LB_RESET 0x17 /* * Firmware options 1, 2, 3. */ @@ -631,8 +689,10 @@ typedef struct { #define MBC_DIAGNOSTIC_LOOP_BACK 0x45 /* Diagnostic loop back. */ #define MBC_ONLINE_SELF_TEST 0x46 /* Online self-test. */ #define MBC_ENHANCED_GET_PORT_DATABASE 0x47 /* Get port database + login */ +#define MBC_CONFIGURE_VF 0x4b /* Configure VFs */ #define MBC_RESET_LINK_STATUS 0x52 /* Reset Link Error Status */ #define MBC_IOCB_COMMAND_A64 0x54 /* Execute IOCB command (64) */ +#define MBC_PORT_LOGOUT 0x56 /* Port Logout request */ #define MBC_SEND_RNID_ELS 0x57 /* Send RNID ELS request */ #define MBC_SET_RNID_PARAMS 0x59 /* Set RNID parameters */ #define MBC_GET_RNID_PARAMS 0x5a /* Data Rate */ @@ -673,6 +733,7 @@ typedef struct { #define MBC_GET_TIMEOUT_PARAMS 0x22 /* Get FW timeouts. */ #define MBC_TRACE_CONTROL 0x27 /* Trace control command. */ #define MBC_GEN_SYSTEM_ERROR 0x2a /* Generate System Error. */ +#define MBC_WRITE_SFP 0x30 /* Write SFP Data. */ #define MBC_READ_SFP 0x31 /* Read SFP Data. */ #define MBC_SET_TIMEOUT_PARAMS 0x32 /* Set FW timeouts. */ #define MBC_MID_INITIALIZE_FIRMWARE 0x48 /* MID Initialize firmware. */ @@ -682,6 +743,13 @@ typedef struct { #define MBC_SEND_RNFT_ELS 0x5e /* Send RNFT ELS request */ #define MBC_GET_LINK_PRIV_STATS 0x6d /* Get link & private data. */ #define MBC_SET_VENDOR_ID 0x76 /* Set Vendor ID. */ +#define MBC_SET_PORT_CONFIG 0x122 /* Set port configuration */ +#define MBC_GET_PORT_CONFIG 0x123 /* Get port configuration */ + +/* + * ISP81xx mailbox commands + */ +#define MBC_WRITE_MPI_REGISTER 0x01 /* Write MPI Register. */ /* Firmware return data sizes */ #define FCAL_MAP_SIZE 128 @@ -926,7 +994,8 @@ struct link_statistics { uint32_t prim_seq_err_cnt; uint32_t inval_xmit_word_cnt; uint32_t inval_crc_cnt; - uint32_t unused1[0x1b]; + uint32_t lip_cnt; + uint32_t unused1[0x1a]; uint32_t tx_frames; uint32_t rx_frames; uint32_t dumped_frames; @@ -1201,6 +1270,7 @@ typedef struct { #define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */ } response_t; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET /* * ISP queue - ATIO queue entry definition. */ @@ -1211,6 +1281,7 @@ typedef struct { uint32_t signature; #define ATIO_PROCESSED 0xDEADDEAD /* Signature */ } atio_t; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ typedef union { uint16_t extended; @@ -1330,6 +1401,66 @@ typedef struct { uint32_t dseg_4_length; /* Data segment 4 length. */ } cont_a64_entry_t; +#define PO_MODE_DIF_INSERT 0 +#define PO_MODE_DIF_REMOVE BIT_0 +#define PO_MODE_DIF_PASS BIT_1 +#define PO_MODE_DIF_REPLACE (BIT_0 + BIT_1) +#define PO_ENABLE_DIF_BUNDLING BIT_8 +#define PO_ENABLE_INCR_GUARD_SEED BIT_3 +#define PO_DISABLE_INCR_REF_TAG BIT_5 +#define PO_DISABLE_GUARD_CHECK BIT_4 +/* + * ISP queue - 64-Bit addressing, continuation crc entry structure definition. + */ +struct crc_context { + uint32_t handle; /* System handle. */ + uint32_t ref_tag; + uint16_t app_tag; + uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/ + uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/ + uint16_t guard_seed; /* Initial Guard Seed */ + uint16_t prot_opts; /* Requested Data Protection Mode */ + uint16_t blk_size; /* Data size in bytes */ + uint16_t runt_blk_guard; /* Guard value for runt block (tape + * only) */ + uint32_t byte_count; /* Total byte count/ total data + * transfer count */ + union { + struct { + uint32_t reserved_1; + uint16_t reserved_2; + uint16_t reserved_3; + uint32_t reserved_4; + uint32_t data_address[2]; + uint32_t data_length; + uint32_t reserved_5[2]; + uint32_t reserved_6; + } nobundling; + struct { + uint32_t dif_byte_count; /* Total DIF byte + * count */ + uint16_t reserved_1; + uint16_t dseg_count; /* Data segment count */ + uint32_t reserved_2; + uint32_t data_address[2]; + uint32_t data_length; + uint32_t dif_address[2]; + uint32_t dif_length; /* Data segment 0 + * length */ + } bundling; + } u; + + struct fcp_cmnd fcp_cmnd; + dma_addr_t crc_ctx_dma; + /* List of DMA context transfers */ + struct list_head dsd_list; + + /* This structure should not exceed 512 bytes */ +}; + +#define CRC_CONTEXT_LEN_FW (offsetof(struct crc_context, fcp_cmnd.lun)) +#define CRC_CONTEXT_FCPCMND_OFF (offsetof(struct crc_context, fcp_cmnd.lun)) + /* * ISP queue - status entry structure definition. */ @@ -1390,6 +1521,7 @@ typedef struct { #define CS_ABORTED 0x5 /* System aborted command. */ #define CS_TIMEOUT 0x6 /* Timeout error. */ #define CS_DATA_OVERRUN 0x7 /* Data overrun. */ +#define CS_DIF_ERROR 0xC /* DIF error detected */ #define CS_DATA_UNDERRUN 0x15 /* Data Underrun. */ #define CS_QUEUE_FULL 0x1C /* Queue Full. */ @@ -1583,8 +1715,14 @@ typedef struct { uint8_t port_name[WWN_SIZE]; uint8_t fabric_port_name[WWN_SIZE]; uint16_t fp_speed; + uint8_t fc4_type; } sw_info_t; +/* FCP-4 types */ +#define FC4_TYPE_FCP_SCSI 0x08 +#define FC4_TYPE_OTHER 0x0 +#define FC4_TYPE_UNKNOWN 0xff + /* * Fibre channel port type. */ @@ -1602,16 +1740,20 @@ typedef struct { */ typedef struct fc_port { struct list_head list; - struct scsi_qla_host *ha; + struct scsi_qla_host *vha; uint8_t node_name[WWN_SIZE]; uint8_t port_name[WWN_SIZE]; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET /* True, if confirmed completion is supported */ uint8_t conf_compl_supported:1; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ port_id_t d_id; uint16_t loop_id; uint16_t old_loop_id; + uint8_t fcp_prio; + uint8_t fabric_port_name[WWN_SIZE]; uint16_t fp_speed; @@ -1620,20 +1762,18 @@ typedef struct fc_port { atomic_t state; uint32_t flags; - int port_login_retry_count; int login_retry; - atomic_t port_down_timer; struct fc_rport *rport, *drport; u32 supported_classes; - unsigned long last_queue_full; - unsigned long last_ramp_up; - - struct list_head vp_fcport; - uint16_t vp_idx; + uint8_t fc4_type; + uint8_t scan_state; } fc_port_t; +#define QLA_FCPORT_SCAN_NONE 0 +#define QLA_FCPORT_SCAN_FOUND 1 + /* * Fibre channel port/lun states. */ @@ -1641,39 +1781,23 @@ typedef struct fc_port { #define FCS_DEVICE_DEAD 2 #define FCS_DEVICE_LOST 3 #define FCS_ONLINE 4 -#define FCS_NOT_SUPPORTED 5 -#define FCS_FAILOVER 6 -#define FCS_FAILOVER_FAILED 7 + +static const char *port_state_str[] = { + "Unknown", + "UNCONFIGURED", + "DEAD", + "LOST", + "ONLINE" +}; /* * FC port flags. */ #define FCF_FABRIC_DEVICE BIT_0 #define FCF_LOGIN_NEEDED BIT_1 -#define FCF_FO_MASKED BIT_2 -#define FCF_FAILOVER_NEEDED BIT_3 -#define FCF_RESET_NEEDED BIT_4 -#define FCF_PERSISTENT_BOUND BIT_5 -#define FCF_TAPE_PRESENT BIT_6 -#define FCF_FARP_DONE BIT_7 -#define FCF_FARP_FAILED BIT_8 -#define FCF_FARP_REPLY_NEEDED BIT_9 +#define FCF_FCP2_DEVICE BIT_2 +#define FCF_ASYNC_SENT BIT_3 #define FCF_AUTH_REQ BIT_10 -#define FCF_SEND_AUTH_REQ BIT_11 -#define FCF_RECEIVE_AUTH_REQ BIT_12 -#define FCF_AUTH_SUCCESS BIT_13 -#define FCF_RLC_SUPPORT BIT_14 -#define FCF_CONFIG BIT_15 /* Needed? */ -#define FCF_RESCAN_NEEDED BIT_16 -#define FCF_XP_DEVICE BIT_17 -#define FCF_MSA_DEVICE BIT_18 -#define FCF_EVA_DEVICE BIT_19 -#define FCF_MSA_PORT_ACTIVE BIT_20 -#define FCF_FAILBACK_DISABLE BIT_21 -#define FCF_FAILOVER_DISABLE BIT_22 -#define FCF_DSXXX_DEVICE BIT_23 -#define FCF_AA_EVA_DEVICE BIT_24 -#define FCF_AA_MSA_DEVICE BIT_25 /* No loop ID flag. */ #define FC_NO_LOOP_ID 0x1000 @@ -1701,7 +1825,6 @@ typedef struct fc_port { #define GID_PT_CMD 0x1A1 #define GID_PT_REQ_SIZE (16 + 4) -#define GID_PT_RSP_SIZE (16 + (MAX_FIBRE_DEVICES * 4)) #define GPN_ID_CMD 0x112 #define GPN_ID_REQ_SIZE (16 + 4) @@ -1739,6 +1862,9 @@ typedef struct fc_port { #define GPSC_REQ_SIZE (16 + 8) #define GPSC_RSP_SIZE (16 + 2 + 2) +#define GFF_ID_CMD 0x011F +#define GFF_ID_REQ_SIZE (16 + 4) +#define GFF_ID_RSP_SIZE (16 + 128) /* * HBA attribute types. @@ -1940,6 +2066,11 @@ struct ct_sns_req { struct { uint8_t port_name[8]; } gpsc; + + struct { + uint8_t reserved; + uint8_t port_name[3]; + } gff_id; } req; }; @@ -1983,7 +2114,9 @@ struct ct_sns_rsp { } ga_nxt; struct { - struct ct_sns_gid_pt_data entries[MAX_FIBRE_DEVICES]; + /* Assume the largest number of targets for the union */ + struct ct_sns_gid_pt_data + entries[MAX_FIBRE_DEVICES_MAX]; } gid_pt; struct { @@ -2012,6 +2145,11 @@ struct ct_sns_rsp { uint16_t speeds; uint16_t speed; } gpsc; + +#define GFF_FCP_SCSI_OFFSET 7 + struct { + uint8_t fc4_features[128]; + } gff_id; } rsp; }; @@ -2039,7 +2177,11 @@ struct ct_sns_pkt { #define GID_PT_SNS_SCMD_LEN 6 #define GID_PT_SNS_CMD_SIZE 28 -#define GID_PT_SNS_DATA_SIZE (MAX_FIBRE_DEVICES * 4 + 16) +/* + * Assume MAX_FIBRE_DEVICES_2100 as these defines are only used with older + * adapters. + */ +#define GID_PT_SNS_DATA_SIZE (MAX_FIBRE_DEVICES_2100 * 4 + 16) #define GPN_ID_SNS_SCMD_LEN 6 #define GPN_ID_SNS_CMD_SIZE 28 @@ -2087,7 +2229,6 @@ struct gid_list_info { uint16_t loop_id; /* ISP23XX -- 6 bytes. */ uint16_t reserved_1; /* ISP24XX -- 8 bytes. */ }; -#define GID_LIST_SIZE (sizeof(struct gid_list_info) * MAX_FIBRE_DEVICES) /* NPIV */ typedef struct vport_info { @@ -2117,6 +2258,8 @@ typedef struct vport_params { #define VP_RET_CODE_NO_MEM 5 #define VP_RET_CODE_NOT_FOUND 6 +struct qla_hw_data; +struct rsp_que; /* * ISP operations */ @@ -2135,12 +2278,12 @@ struct isp_operations { char * (*fw_version_str) (struct scsi_qla_host *, char *, int); irq_handler_t intr_handler; - void (*enable_intrs) (struct scsi_qla_host *); - void (*disable_intrs) (struct scsi_qla_host *); + void (*enable_intrs) (struct qla_hw_data *); + void (*disable_intrs) (struct qla_hw_data *); - int (*abort_command) (struct scsi_qla_host *, srb_t *); - int (*target_reset) (struct fc_port *, unsigned int); - int (*lun_reset) (struct fc_port *, unsigned int); + int (*abort_command) (srb_t *); + int (*target_reset) (struct fc_port *, unsigned int, int); + int (*lun_reset) (struct fc_port *, unsigned int, int); int (*fabric_login) (struct scsi_qla_host *, uint16_t, uint8_t, uint8_t, uint8_t, uint16_t *, uint8_t); int (*fabric_logout) (struct scsi_qla_host *, uint16_t, uint8_t, @@ -2169,6 +2312,9 @@ struct isp_operations { uint32_t); int (*get_flash_version) (struct scsi_qla_host *, void *); + int (*start_scsi) (srb_t *); + int (*abort_isp) (struct scsi_qla_host *); + int (*iospace_config)(struct qla_hw_data*); }; /* MSI-X Support *************************************************************/ @@ -2180,16 +2326,18 @@ struct isp_operations { #define QLA_MSIX_DEFAULT 0x00 #define QLA_MSIX_RSP_Q 0x01 -#define QLA_MSIX_ENTRIES 2 #define QLA_MIDX_DEFAULT 0 #define QLA_MIDX_RSP_Q 1 +#define QLA_PCI_MSIX_CONTROL 0xa2 +#define QLA_83XX_PCI_MSIX_CONTROL 0x92 struct scsi_qla_host; struct qla_msix_entry { int have_irq; - uint16_t msix_vector; - uint16_t msix_entry; + uint32_t vector; + uint16_t entry; + struct rsp_que *rsp; }; #define WATCH_INTERVAL 1 /* number of seconds */ @@ -2197,7 +2345,14 @@ struct qla_msix_entry { /* Work events. */ enum qla_work_type { QLA_EVT_AEN, - QLA_EVT_HWE_LOG, + QLA_EVT_IDC_ACK, + QLA_EVT_ASYNC_LOGIN, + QLA_EVT_ASYNC_LOGIN_DONE, + QLA_EVT_ASYNC_LOGOUT, + QLA_EVT_ASYNC_LOGOUT_DONE, + QLA_EVT_ASYNC_ADISC, + QLA_EVT_ASYNC_ADISC_DONE, + QLA_EVT_UEVENT, }; @@ -2213,9 +2368,18 @@ struct qla_work_evt { u32 data; } aen; struct { - uint16_t code; - uint16_t d1, d2, d3; - } hwe; +#define QLA_IDC_ACK_REGS 7 + uint16_t mb[QLA_IDC_ACK_REGS]; + } idc_ack; + struct { + struct fc_port *fcport; +#define QLA_LOGIO_LOGIN_RETRIED BIT_0 + u16 data[2]; + } logio; + struct { + u32 code; +#define QLA_UEVENT_CODE_FW_DUMP 0 + } uevent; } u; }; @@ -2234,235 +2398,217 @@ struct qla_chip_state_84xx { uint32_t gold_fw_version; }; +struct qla_statistics { + uint32_t total_isp_aborts; + uint64_t input_bytes; + uint64_t output_bytes; +}; + +/* Multi queue support */ +#define MBC_INITIALIZE_MULTIQ 0x1f +#define QLA_QUE_PAGE 0X1000 +#define QLA_MQ_SIZE 32 +#define QLA_MAX_QUEUES 256 +#define ISP_QUE_REG(ha, id) \ + ((ha->mqenable || IS_QLA83XX(ha)) ? \ + ((void *)(ha->mqiobase) +\ + (QLA_QUE_PAGE * id)) :\ + ((void *)(ha->iobase))) +#define QLA_REQ_QUE_ID(tag) \ + ((tag < QLA_MAX_QUEUES && tag > 0) ? tag : 0) +#define QLA_DEFAULT_QUE_QOS 5 +#define QLA_PRECONFIG_VPORTS 32 +#define QLA_MAX_VPORTS_QLA24XX 128 +#define QLA_MAX_VPORTS_QLA25XX 256 +/* Response queue data structure */ +struct rsp_que { + dma_addr_t dma; + response_t *ring; + response_t *ring_ptr; + uint32_t __iomem *rsp_q_in; /* FWI2-capable only. */ + uint32_t __iomem *rsp_q_out; + uint16_t ring_index; + uint16_t out_ptr; + uint16_t length; + uint16_t options; + uint16_t rid; + uint16_t id; + uint16_t vp_idx; + struct qla_hw_data *hw; + struct qla_msix_entry *msix; + struct req_que *req; + srb_t *status_srb; /* status continuation entry */ + struct work_struct q_work; +}; + +/* Request queue data structure */ +struct req_que { + dma_addr_t dma; + request_t *ring; + request_t *ring_ptr; + uint32_t __iomem *req_q_in; /* FWI2-capable only. */ + uint32_t __iomem *req_q_out; + uint16_t ring_index; + uint16_t in_ptr; + uint16_t cnt; + uint16_t length; + uint16_t options; + uint16_t rid; + uint16_t id; + uint16_t qos; + uint16_t vp_idx; + struct rsp_que *rsp; + srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS]; + uint32_t current_outstanding_cmd; + int max_q_depth; +}; + +/* Place holder for FW buffer parameters */ +struct qlfc_fw { + void *fw_buf; + dma_addr_t fw_dma; + uint32_t len; +}; + /* - * Linux Host Adapter structure - */ -typedef struct scsi_qla_host { - struct list_head list; - - /* Commonly used flags and state information. */ - struct Scsi_Host *host; - struct pci_dev *pdev; - - unsigned long host_no; - unsigned long instance; + * Qlogic host adapter specific data structure. +*/ +struct qla_hw_data { + struct pci_dev *pdev; + /* SRB cache. */ +#define SRB_MIN_REQ 128 + mempool_t *srb_mempool; volatile struct { - uint32_t init_done :1; - uint32_t online :1; uint32_t mbox_int :1; uint32_t mbox_busy :1; - uint32_t rscn_queue_overflow :1; - uint32_t reset_active :1; - - uint32_t management_server_logged_in :1; - uint32_t process_response_queue :1; - uint32_t disable_risc_code_load :1; - uint32_t enable_64bit_addressing :1; uint32_t enable_lip_reset :1; - uint32_t enable_lip_full_login :1; uint32_t enable_target_reset :1; + uint32_t enable_lip_full_login :1; uint32_t enable_led_scheme :1; - uint32_t inta_enabled :1; + uint32_t msi_enabled :1; uint32_t msix_enabled :1; uint32_t disable_serdes :1; uint32_t gpsc_supported :1; - uint32_t vsan_enabled :1; uint32_t npiv_supported :1; + uint32_t pci_channel_io_perm_failure:1; uint32_t fce_enabled :1; - uint32_t hw_event_marker_found :1; + uint32_t fac_supported :1; + + uint32_t chip_reset_done :1; + uint32_t port0 :1; + uint32_t running_gold_fw :1; + uint32_t eeh_busy :1; + uint32_t cpu_affinity_enabled :1; + uint32_t fcp_prio_enabled :1; + uint32_t isp82xx_fw_hung:1; + + uint32_t quiesce_owner:1; + uint32_t thermal_supported:1; + uint32_t isp82xx_reset_hdlr_active:1; + uint32_t isp82xx_reset_owner:1; + uint32_t isp82xx_no_md_cap:1; + /* 27 bits */ } flags; - atomic_t loop_state; -#define LOOP_TIMEOUT 1 -#define LOOP_DOWN 2 -#define LOOP_UP 3 -#define LOOP_UPDATE 4 -#define LOOP_READY 5 -#define LOOP_DEAD 6 - - unsigned long dpc_flags; -#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */ -#define RESET_ACTIVE 1 -#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */ -#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */ -#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */ -#define LOOP_RESYNC_ACTIVE 5 -#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */ -#define RSCN_UPDATE 7 /* Perform an RSCN update. */ -#define MAILBOX_RETRY 8 -#define ISP_RESET_NEEDED 9 /* Initiate a ISP reset. */ -#define FAILOVER_EVENT_NEEDED 10 -#define FAILOVER_EVENT 11 -#define FAILOVER_NEEDED 12 -#define SCSI_RESTART_NEEDED 13 /* Processes SCSI retry queue. */ -#define PORT_RESTART_NEEDED 14 /* Processes Retry queue. */ -#define RESTART_QUEUES_NEEDED 15 /* Restarts the Lun queue. */ -#define ABORT_QUEUES_NEEDED 16 -#define RELOGIN_NEEDED 17 -#define LOGIN_RETRY_NEEDED 18 /* Initiate required fabric logins. */ -#define REGISTER_FC4_NEEDED 19 /* SNS FC4 registration required. */ -#define ISP_ABORT_RETRY 20 /* ISP aborted. */ -#define FCPORT_RESCAN_NEEDED 21 /* IO descriptor processing needed */ -#define IODESC_PROCESS_NEEDED 22 /* IO descriptor processing needed */ -#define IOCTL_ERROR_RECOVERY 23 -#define LOOP_RESET_NEEDED 24 -#define BEACON_BLINK_NEEDED 25 -#define REGISTER_FDMI_NEEDED 26 -#define FCPORT_UPDATE_NEEDED 27 -#define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */ -#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */ - - uint32_t device_flags; -#define DFLG_LOCAL_DEVICES BIT_0 -#define DFLG_RETRY_LOCAL_DEVICES BIT_1 -#define DFLG_FABRIC_DEVICES BIT_2 -#define SWITCH_FOUND BIT_3 -#define DFLG_NO_CABLE BIT_4 - -#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532 -#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432 - uint32_t device_type; -#define DT_ISP2100 BIT_0 -#define DT_ISP2200 BIT_1 -#define DT_ISP2300 BIT_2 -#define DT_ISP2312 BIT_3 -#define DT_ISP2322 BIT_4 -#define DT_ISP6312 BIT_5 -#define DT_ISP6322 BIT_6 -#define DT_ISP2422 BIT_7 -#define DT_ISP2432 BIT_8 -#define DT_ISP5422 BIT_9 -#define DT_ISP5432 BIT_10 -#define DT_ISP2532 BIT_11 -#define DT_ISP8432 BIT_12 -#define DT_ISP8021 BIT_14 -#define DT_ISP_LAST (DT_ISP8432 << 1) - -#define DT_IIDMA BIT_26 -#define DT_FWI2 BIT_27 -#define DT_ZIO_SUPPORTED BIT_28 -#define DT_OEM_001 BIT_29 -#define DT_ISP2200A BIT_30 -#define DT_EXTENDED_IDS BIT_31 - -#define DT_MASK(ha) ((ha)->device_type & (DT_ISP_LAST - 1)) -#define IS_QLA2100(ha) (DT_MASK(ha) & DT_ISP2100) -#define IS_QLA2200(ha) (DT_MASK(ha) & DT_ISP2200) -#define IS_QLA2300(ha) (DT_MASK(ha) & DT_ISP2300) -#define IS_QLA2312(ha) (DT_MASK(ha) & DT_ISP2312) -#define IS_QLA2322(ha) (DT_MASK(ha) & DT_ISP2322) -#define IS_QLA6312(ha) (DT_MASK(ha) & DT_ISP6312) -#define IS_QLA6322(ha) (DT_MASK(ha) & DT_ISP6322) -#define IS_QLA2422(ha) (DT_MASK(ha) & DT_ISP2422) -#define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432) -#define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422) -#define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432) -#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532) -#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432) -#define IS_QLA82XX(ha) (DT_MASK(ha) & DT_ISP8021) - -#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ - IS_QLA6312(ha) || IS_QLA6322(ha)) -#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha)) -#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha)) -#define IS_QLA25XX(ha) (IS_QLA2532(ha)) -#define IS_QLA84XX(ha) (IS_QLA8432(ha)) -#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ - IS_QLA84XX(ha)) - -#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA) -#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2) -#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED) -#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001) -#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS) - - /* SRB cache. */ -#define SRB_MIN_REQ 128 - mempool_t *srb_mempool; - /* This spinlock is used to protect "io transactions", you must - * acquire it before doing any IO to the card, eg with RD_REG*() and - * WRT_REG*() for the duration of your entire commandtransaction. - * - * This spinlock is of lower priority than the io request lock. - */ + * acquire it before doing any IO to the card, eg with RD_REG*() and + * WRT_REG*() for the duration of your entire commandtransaction. + * + * This spinlock is of lower priority than the io request lock. + */ - spinlock_t hardware_lock ____cacheline_aligned; + spinlock_t hardware_lock ____cacheline_aligned; + device_reg_t __iomem *iobase; /* Base I/O address */ - int bars; - int mem_only; - device_reg_t __iomem *iobase; /* Base I/O address */ - resource_size_t pio_address; -#define MIN_IOBASE_LEN 0x100 - - /* ISP ring lock, rings, and indexes */ - dma_addr_t request_dma; /* Physical address. */ - request_t *request_ring; /* Base virtual address */ - request_t *request_ring_ptr; /* Current address. */ - uint16_t req_ring_index; /* Current index. */ - uint16_t req_q_cnt; /* Number of available entries. */ - uint16_t request_q_length; - - dma_addr_t response_dma; /* Physical address. */ - response_t *response_ring; /* Base virtual address */ - response_t *response_ring_ptr; /* Current address. */ - uint16_t rsp_ring_index; /* Current index. */ - uint16_t response_q_length; - - /* Protected by hw lock */ + unsigned int enable_64bit_addressing :1; unsigned int enable_class_2 :1; + unsigned int disable_msix_handshake :1; #ifdef CONFIG_SCSI_QLA2XXX_TARGET unsigned int enable_explicit_conf :1; unsigned int host_shutting_down :1; - unsigned int ini_mode_force_reverse :1; - unsigned int node_name_set :1; + unsigned int atio_ignored :1; dma_addr_t atio_dma; /* Physical address. */ atio_t *atio_ring; /* Base virtual address */ atio_t *atio_ring_ptr; /* Current address. */ uint16_t atio_ring_index; /* Current index. */ uint16_t atio_q_length; + uint32_t __iomem *atio_q_in; + uint32_t __iomem *atio_q_out; + + /* Protected by hardware_lock */ + struct list_head unknown_atio_list; /* - * Processing Q2T tgt reference. NULL on not enabled targets. Protected - * by tgt_mutex AND hardware_lock for writing and tgt_mutex OR - * hardware_lock for reading. + * It should be here, because targets can be on virtual port of + * a physical port without target. */ - struct q2t_tgt *tgt; + struct delayed_work unknown_atio_work; - struct q2t_cmd *cmds[MAX_OUTSTANDING_COMMANDS]; - uint16_t current_handle; + struct qla_tgt_vp_map *tgt_vp_map; + + struct list_head ha_list_entry; + + int saved_set; + uint16_t saved_exchange_count; + uint32_t saved_firmware_options_1; + uint32_t saved_firmware_options_2; + uint32_t saved_firmware_options_3; + uint8_t saved_firmware_options[2]; + uint8_t saved_add_firmware_options[2]; + + uint8_t orig_hw_port_name[WWN_SIZE]; + uint8_t orig_hw_node_name[WWN_SIZE]; #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - struct isp_operations *isp_ops; + int bars; + int mem_only; + resource_size_t pio_address; - /* Outstandings ISP commands. */ - srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS]; - uint32_t current_outstanding_cmd; - srb_t *status_srb; /* Status continuation entry. */ +#define MIN_IOBASE_LEN 0x100 +/* Multi queue data structs */ + device_reg_t __iomem *mqiobase; + device_reg_t __iomem *msixbase; + uint16_t msix_count; + uint8_t mqenable; + struct req_que **req_q_map; + struct rsp_que **rsp_q_map; + unsigned long req_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)]; + unsigned long rsp_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)]; + uint8_t max_req_queues; + uint8_t max_rsp_queues; + struct qla_npiv_entry *npiv_info; + uint16_t nvram_npiv_size; + + uint16_t switch_cap; +#define FLOGI_SEQ_DEL BIT_8 +#define FLOGI_MID_SUPPORT BIT_10 +#define FLOGI_VSAN_SUPPORT BIT_12 +#define FLOGI_SP_SUPPORT BIT_13 + + uint8_t port_no; /* Physical port of adapter */ + + /* Timeout timers. */ + uint8_t loop_down_abort_time; /* port down timer */ + atomic_t loop_down_timer; /* loop down timer */ + uint8_t link_down_timeout; /* link down timeout */ + uint16_t max_loop_id; + uint16_t max_fibre_devices; /* Maximum number of targets */ - /* ISP configuration data. */ - uint16_t loop_id; /* Host adapter loop id */ - uint16_t switch_cap; -#define FLOGI_SEQ_DEL BIT_8 -#define FLOGI_MID_SUPPORT BIT_10 -#define FLOGI_VSAN_SUPPORT BIT_12 -#define FLOGI_SP_SUPPORT BIT_13 uint16_t fb_rev; - - port_id_t d_id; /* Host adapter port id */ - uint16_t max_public_loop_ids; - uint16_t min_external_loopid; /* First external loop Id */ + uint16_t min_external_loopid; /* First external loop Id */ #define PORT_SPEED_UNKNOWN 0xFFFF -#define PORT_SPEED_1GB 0x00 -#define PORT_SPEED_2GB 0x01 -#define PORT_SPEED_4GB 0x03 -#define PORT_SPEED_8GB 0x04 - uint16_t link_data_rate; /* F/W operating speed */ +#define PORT_SPEED_1GB 0x00 +#define PORT_SPEED_2GB 0x01 +#define PORT_SPEED_4GB 0x03 +#define PORT_SPEED_8GB 0x04 +#define PORT_SPEED_16GB 0x05 +#define PORT_SPEED_10GB 0x13 + uint16_t link_data_rate; /* F/W operating speed */ uint8_t current_topology; uint8_t prev_topology; @@ -2471,15 +2617,96 @@ typedef struct scsi_qla_host { #define ISP_CFG_FL 4 #define ISP_CFG_F 8 - uint8_t operating_mode; /* F/W operating mode */ + uint8_t operating_mode; /* F/W operating mode */ #define LOOP 0 #define P2P 1 #define LOOP_P2P 2 #define P2P_LOOP 3 - - uint8_t marker_needed; - uint8_t interrupts_on; + uint32_t isp_abort_cnt; + +#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532 +#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432 +#define PCI_DEVICE_ID_QLOGIC_ISP8001 0x8001 +#define PCI_DEVICE_ID_QLOGIC_ISP8031 0x8031 +#define PCI_DEVICE_ID_QLOGIC_ISP2031 0x2031 + uint32_t device_type; +#define DT_ISP2100 BIT_0 +#define DT_ISP2200 BIT_1 +#define DT_ISP2300 BIT_2 +#define DT_ISP2312 BIT_3 +#define DT_ISP2322 BIT_4 +#define DT_ISP6312 BIT_5 +#define DT_ISP6322 BIT_6 +#define DT_ISP2422 BIT_7 +#define DT_ISP2432 BIT_8 +#define DT_ISP5422 BIT_9 +#define DT_ISP5432 BIT_10 +#define DT_ISP2532 BIT_11 +#define DT_ISP8432 BIT_12 +#define DT_ISP8001 BIT_13 +#define DT_ISP8021 BIT_14 +#define DT_ISP2031 BIT_15 +#define DT_ISP8031 BIT_16 +#define DT_ISP_LAST (DT_ISP8031 << 1) + +#define DT_T10_PI BIT_25 +#define DT_IIDMA BIT_26 +#define DT_FWI2 BIT_27 +#define DT_ZIO_SUPPORTED BIT_28 +#define DT_OEM_001 BIT_29 +#define DT_ISP2200A BIT_30 +#define DT_EXTENDED_IDS BIT_31 +#define DT_MASK(ha) ((ha)->device_type & (DT_ISP_LAST - 1)) +#define IS_QLA2100(ha) (DT_MASK(ha) & DT_ISP2100) +#define IS_QLA2200(ha) (DT_MASK(ha) & DT_ISP2200) +#define IS_QLA2300(ha) (DT_MASK(ha) & DT_ISP2300) +#define IS_QLA2312(ha) (DT_MASK(ha) & DT_ISP2312) +#define IS_QLA2322(ha) (DT_MASK(ha) & DT_ISP2322) +#define IS_QLA6312(ha) (DT_MASK(ha) & DT_ISP6312) +#define IS_QLA6322(ha) (DT_MASK(ha) & DT_ISP6322) +#define IS_QLA2422(ha) (DT_MASK(ha) & DT_ISP2422) +#define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432) +#define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422) +#define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432) +#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532) +#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432) +#define IS_QLA8001(ha) (DT_MASK(ha) & DT_ISP8001) +#define IS_QLA81XX(ha) (IS_QLA8001(ha)) +#define IS_QLA82XX(ha) (DT_MASK(ha) & DT_ISP8021) +#define IS_QLA2031(ha) (DT_MASK(ha) & DT_ISP2031) +#define IS_QLA8031(ha) (DT_MASK(ha) & DT_ISP8031) + +#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ + IS_QLA6312(ha) || IS_QLA6322(ha)) +#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha)) +#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha)) +#define IS_QLA25XX(ha) (IS_QLA2532(ha)) +#define IS_QLA83XX(ha) (IS_QLA2031(ha) || IS_QLA8031(ha)) +#define IS_QLA84XX(ha) (IS_QLA8432(ha)) +#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ + IS_QLA84XX(ha)) +#define IS_CNA_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha) || \ + IS_QLA8031(ha)) +#define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \ + IS_QLA25XX(ha) || IS_QLA81XX(ha) || \ + IS_QLA82XX(ha) || IS_QLA83XX(ha)) +#define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) +#define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || \ + IS_QLA83XX(ha)) && (ha)->flags.msix_enabled) +#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) +#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) +#define IS_ALOGIO_CAPABLE(ha) (IS_QLA23XX(ha) || IS_FWI2_CAPABLE(ha)) + +#define IS_T10_PI_CAPABLE(ha) ((ha)->device_type & DT_T10_PI) +#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA) +#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2) +#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED) +#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001) +#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS) +#define IS_CT6_SUPPORTED(ha) ((ha)->device_type & DT_CT6_SUPPORTED) +#define IS_MQUE_CAPABLE(ha) ((ha)->mqenable || IS_QLA83XX(ha)) +#define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha)) /* HBA serial number */ uint8_t serial0; @@ -2487,8 +2714,8 @@ typedef struct scsi_qla_host { uint8_t serial2; /* NVRAM configuration data */ -#define MAX_NVRAM_SIZE 4096 -#define VPD_OFFSET MAX_NVRAM_SIZE / 2 +#define MAX_NVRAM_SIZE 4096 +#define VPD_OFFSET MAX_NVRAM_SIZE / 2 uint16_t nvram_size; uint16_t nvram_base; void *nvram; @@ -2502,34 +2729,8 @@ typedef struct scsi_qla_host { uint16_t r_a_tov; int port_down_retry_count; uint8_t mbx_count; - uint16_t last_loop_id; - uint16_t mgmt_svr_loop_id; - - uint32_t login_retry_count; - int max_q_depth; - - struct list_head work_list; - - /* - * To surprise, fcports list isn't anyhow protected, although it can be - * accessed from many contexts, including concurrently. Seems, the - * original author of this code thought that if anything never deleted - * from this list, it is always safe to travel over it without any - * protection. Obviously, this isn't true. We will fix - * that making access to fcports list using RCU functions. This isn't - * a complete solution, because it isn't clear if it's safe if some - * function going over this list misses just added entry, but definitely - * better, than currently. Also, most likely, fcports isn't the only - * variable in this structure wrongly accessed in an unprotected - * manner, so ToDo. VLNB. - */ - struct list_head fcports; - - /* RSCN queue. */ - uint32_t rscn_queue[MAX_RSCN_COUNT]; - uint8_t rscn_in_ptr; - uint8_t rscn_out_ptr; + uint32_t login_retry_count; /* SNS command interfaces. */ ms_iocb_entry_t *ms_iocb; dma_addr_t ms_iocb_dma; @@ -2539,77 +2740,81 @@ typedef struct scsi_qla_host { struct sns_cmd_pkt *sns_cmd; dma_addr_t sns_cmd_dma; - char *pass_thru; - dma_addr_t pass_thru_dma; +#define SFP_DEV_SIZE 256 +#define SFP_BLOCK_SIZE 64 + void *sfp_data; + dma_addr_t sfp_data_dma; -#define SFP_DEV_SIZE 256 -#define SFP_BLOCK_SIZE 64 - void *sfp_data; - dma_addr_t sfp_data_dma; +#define XGMAC_DATA_SIZE 4096 + void *xgmac_data; + dma_addr_t xgmac_data_dma; + +#define DCBX_TLV_DATA_SIZE 4096 + void *dcbx_tlv; + dma_addr_t dcbx_tlv_dma; spinlock_t dpc_lock; struct task_struct *dpc_thread; uint8_t dpc_active; /* DPC routine is active */ - /* Timeout timers. */ - uint8_t loop_down_abort_time; /* port down timer */ - atomic_t loop_down_timer; /* loop down timer */ - uint8_t link_down_timeout; /* link down timeout */ - - uint32_t timer_active; - struct timer_list timer; - dma_addr_t gid_list_dma; struct gid_list_info *gid_list; int gid_list_info_size; /* Small DMA pool allocations -- maximum 256 bytes in length. */ -#define DMA_POOL_SIZE 256 +#define DMA_POOL_SIZE 256 struct dma_pool *s_dma_pool; dma_addr_t init_cb_dma; init_cb_t *init_cb; int init_cb_size; + dma_addr_t ex_init_cb_dma; + struct ex_init_cb_81xx *ex_init_cb; + + void *async_pd; + dma_addr_t async_pd_dma; + + void *swl; /* These are used by mailbox operations. */ volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT]; mbx_cmd_t *mcp; unsigned long mbx_cmd_flags; -#define MBX_INTERRUPT 1 -#define MBX_INTR_WAIT 2 +#define MBX_INTERRUPT 1 +#define MBX_INTR_WAIT 2 #define MBX_UPDATE_FLASH_ACTIVE 3 - struct mutex vport_lock; /* Virtual port synchronization */ - struct completion mbx_cmd_comp; /* Serialize mbx access */ + struct mutex vport_lock; /* Virtual port synchronization */ + spinlock_t vport_slock; /* order is hardware_lock, then vport_slock */ + struct completion mbx_cmd_comp; /* Serialize mbx access */ struct completion mbx_intr_comp; /* Used for completion notification */ - struct completion pass_thru_intr_comp; /* For pass thru notification */ - - uint32_t mbx_flags; -#define MBX_IN_PROGRESS BIT_0 -#define MBX_BUSY BIT_1 /* Got the Access */ -#define MBX_SLEEPING_ON_SEM BIT_2 -#define MBX_POLLING_FOR_COMP BIT_3 -#define MBX_COMPLETED BIT_4 -#define MBX_TIMEDOUT BIT_5 -#define MBX_ACCESS_TIMEDOUT BIT_6 + struct completion dcbx_comp; /* For set port config notification */ + int notify_dcbx_comp; /* Basic firmware related information. */ uint16_t fw_major_version; uint16_t fw_minor_version; uint16_t fw_subminor_version; uint16_t fw_attributes; + uint16_t fw_attributes_h; + uint16_t fw_attributes_ext[2]; uint32_t fw_memory_size; uint32_t fw_transfer_size; uint32_t fw_srisc_address; #define RISC_START_ADDRESS_2100 0x1000 #define RISC_START_ADDRESS_2300 0x800 #define RISC_START_ADDRESS_2400 0x100000 + uint16_t fw_xcb_count; - uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */ + uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */ uint8_t fw_seriallink_options[4]; uint16_t fw_seriallink_options24[4]; + uint8_t mpi_version[3]; + uint32_t mpi_capabilities; + uint8_t phy_version[3]; + /* Firmware dump information. */ struct qla2xxx_fw_dump *fw_dump; uint32_t fw_dump_len; @@ -2618,6 +2823,7 @@ typedef struct scsi_qla_host { dma_addr_t eft_dma; void *eft; + uint32_t chain_offset; struct dentry *dfs_dir; struct dentry *dfs_fce; dma_addr_t fce_dma; @@ -2627,12 +2833,6 @@ typedef struct scsi_qla_host { uint64_t fce_wr, fce_rd; struct mutex fce_mutex; - uint32_t hw_event_start; - uint32_t hw_event_ptr; - uint32_t hw_event_pause_errors; - -#define HA_HOST_STR_SIZE 16 - uint8_t host_str[HA_HOST_STR_SIZE]; uint32_t pci_attr; uint16_t chip_revision; @@ -2640,14 +2840,9 @@ typedef struct scsi_qla_host { uint8_t model_number[16+1]; #define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - char *model_desc; + char model_desc[80]; uint8_t adapter_id[16+1]; - uint8_t *node_name; - uint8_t *port_name; - uint8_t fabric_node_name[WWN_SIZE]; - uint32_t isp_abort_cnt; - /* Option ROM information. */ char *optrom_buffer; uint32_t optrom_size; @@ -2658,72 +2853,199 @@ typedef struct scsi_qla_host { uint32_t optrom_region_start; uint32_t optrom_region_size; - /* PCI expansion ROM image information. */ +/* PCI expansion ROM image information. */ #define ROM_CODE_TYPE_BIOS 0 #define ROM_CODE_TYPE_FCODE 1 #define ROM_CODE_TYPE_EFI 3 - uint8_t bios_revision[2]; - uint8_t efi_revision[2]; - uint8_t fcode_revision[16]; + uint8_t bios_revision[2]; + uint8_t efi_revision[2]; + uint8_t fcode_revision[16]; uint32_t fw_revision[4]; - uint16_t fdt_odd_index; + uint32_t gold_fw_version[4]; + + /* Offsets for flash/nvram access (set to ~0 if not used). */ + uint32_t flash_conf_off; + uint32_t flash_data_off; + uint32_t nvram_conf_off; + uint32_t nvram_data_off; + uint32_t fdt_wrt_disable; uint32_t fdt_erase_cmd; uint32_t fdt_block_size; uint32_t fdt_unprotect_sec_cmd; uint32_t fdt_protect_sec_cmd; + uint32_t flt_region_flt; + uint32_t flt_region_fdt; + uint32_t flt_region_boot; + uint32_t flt_region_fw; + uint32_t flt_region_vpd_nvram; + uint32_t flt_region_vpd; + uint32_t flt_region_nvram; + uint32_t flt_region_npiv_conf; + uint32_t flt_region_gold_fw; + uint32_t flt_region_fcp_prio; + uint32_t flt_region_bootload; + /* Needed for BEACON */ - uint16_t beacon_blink_led; - uint8_t beacon_color_state; + uint16_t beacon_blink_led; + uint8_t beacon_color_state; #define QLA_LED_GRN_ON 0x01 #define QLA_LED_YLW_ON 0x02 #define QLA_LED_ABR_ON 0x04 #define QLA_LED_ALL_ON 0x07 /* yellow, green, amber. */ /* ISP2322: red, green, amber. */ + uint16_t zio_mode; + uint16_t zio_timer; - uint16_t zio_mode; - uint16_t zio_timer; - struct fc_host_statistics fc_host_stat; + struct qla_msix_entry *msix_entries; - struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES]; + struct list_head vp_list; /* list of VP */ + unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) / + sizeof(unsigned long)]; + uint16_t num_vhosts; /* number of vports created */ + uint16_t num_vsans; /* number of vsan created */ + uint16_t max_npiv_vports; /* 63 or 125 per topoloty */ + int cur_vport_count; + + struct qla_chip_state_84xx *cs84xx; + struct isp_operations *isp_ops; + struct workqueue_struct *wq; + struct qlfc_fw fw_buf; + + /* FCP_CMND priority support */ + struct qla_fcp_prio_cfg *fcp_prio_cfg; + + struct dma_pool *dl_dma_pool; +#define DSD_LIST_DMA_POOL_SIZE 512 + + struct dma_pool *fcp_cmnd_dma_pool; + mempool_t *ctx_mempool; +#define FCP_CMND_DMA_POOL_SIZE 512 + + unsigned long nx_pcibase; /* Base I/O address */ + uint8_t *nxdb_rd_ptr; /* Doorbell read pointer */ + unsigned long nxdb_wr_ptr; /* Door bell write pointer */ + + uint32_t crb_win; + uint32_t curr_window; + uint32_t ddr_mn_window; + unsigned long mn_win_crb; + unsigned long ms_win_crb; + int qdr_sn_window; + uint32_t nx_dev_init_timeout; + uint32_t nx_reset_timeout; + rwlock_t hw_lock; + uint16_t portnum; /* port number */ + int link_width; + struct fw_blob *hablob; + struct qla82xx_legacy_intr_set nx_legacy_intr; + + uint16_t gbl_dsd_inuse; + uint16_t gbl_dsd_avail; + struct list_head gbl_dsd_list; +#define NUM_DSD_CHAIN 4096 + + uint8_t fw_type; + __le32 file_prd_off; /* File firmware product offset */ + + uint32_t md_template_size; + void *md_tmplt_hdr; + dma_addr_t md_tmplt_hdr_dma; + void *md_dump; + uint32_t md_dump_size; +}; + +/* + * Qlogic scsi host structure + */ +typedef struct scsi_qla_host { + struct list_head list; + struct list_head vp_fcports; /* list of fcports */ + struct list_head work_list; + spinlock_t work_lock; + + /* Commonly used flags and state information. */ + struct Scsi_Host *host; + unsigned long host_no; + uint8_t host_str[16]; + + volatile struct { + uint32_t init_done :1; + uint32_t online :1; + uint32_t reset_active :1; + + uint32_t management_server_logged_in :1; + uint32_t process_response_queue :1; + uint32_t difdix_supported:1; + uint32_t delete_progress:1; + } flags; + + atomic_t loop_state; +#define LOOP_TIMEOUT 1 +#define LOOP_DOWN 2 +#define LOOP_UP 3 +#define LOOP_UPDATE 4 +#define LOOP_READY 5 +#define LOOP_DEAD 6 + + unsigned long dpc_flags; +#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */ +#define RESET_ACTIVE 1 +#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */ +#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */ +#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */ +#define LOOP_RESYNC_ACTIVE 5 +#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */ +#define RSCN_UPDATE 7 /* Perform an RSCN update. */ +#define RELOGIN_NEEDED 8 +#define REGISTER_FC4_NEEDED 9 /* SNS FC4 registration required. */ +#define ISP_ABORT_RETRY 10 /* ISP aborted. */ +#define BEACON_BLINK_NEEDED 11 +#define REGISTER_FDMI_NEEDED 12 +#define FCPORT_UPDATE_NEEDED 13 +#define VP_DPC_NEEDED 14 /* wake up for VP dpc handling */ +#define UNLOADING 15 +#define NPIV_CONFIG_NEEDED 16 +#define ISP_UNRECOVERABLE 17 +#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */ +#define MPI_RESET_NEEDED 19 /* Initiate MPI FW reset */ +#define ISP_QUIESCE_NEEDED 20 /* Driver need some quiescence */ + + uint32_t device_flags; +#define SWITCH_FOUND BIT_0 +#define DFLG_NO_CABLE BIT_1 +#define DFLG_DEV_FAILED BIT_5 + + /* ISP configuration data. */ + uint16_t loop_id; /* Host adapter loop id */ + + port_id_t d_id; /* Host adapter port id */ + uint8_t marker_needed; + uint16_t mgmt_svr_loop_id; + + /* Timeout timers. */ + uint8_t loop_down_abort_time; /* port down timer */ + atomic_t loop_down_timer; /* loop down timer */ + uint8_t link_down_timeout; /* link down timeout */ + + uint32_t timer_active; + struct timer_list timer; + + uint8_t node_name[WWN_SIZE]; + uint8_t port_name[WWN_SIZE]; + uint8_t fabric_node_name[WWN_SIZE]; + + uint16_t fcoe_vlan_id; + uint16_t fcoe_fcf_idx; + uint8_t fcoe_vn_port_mac[6]; + + uint32_t vp_abort_cnt; - struct list_head vp_list; /* list of VP */ struct fc_vport *fc_vport; /* holds fc_vport * for each vport */ - unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) / sizeof(unsigned long)]; - uint16_t num_vhosts; /* number of vports created */ - uint16_t num_vsans; /* number of vsan created */ - uint16_t vp_idx; /* vport ID */ -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - struct qla_tgt_vp_map *tgt_vp_map; - struct mutex tgt_mutex; - - struct mutex tgt_host_action_mutex; - - /* - * Main Q2T tgt reference, which always points to the target, if the - * target mode addon loaded. Protected by tgt_host_action_mutex. - */ - struct q2t_tgt *q2t_tgt; - - struct list_head ha_list_entry; - - int saved_set; - uint16_t saved_exchange_count; - uint32_t saved_firmware_options_1; - uint32_t saved_firmware_options_2; - uint32_t saved_firmware_options_3; - uint8_t saved_firmware_options[2]; - uint8_t saved_add_firmware_options[2]; - - uint8_t tgt_node_name[WWN_SIZE]; -#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - - struct scsi_qla_host *parent; /* holds pport */ unsigned long vp_flags; - struct list_head vp_fcports; /* list of fcports */ #define VP_IDX_ACQUIRED 0 /* bit no 0 */ #define VP_CREATE_NEEDED 1 #define VP_BIND_NEEDED 2 @@ -2742,14 +3064,42 @@ typedef struct scsi_qla_host { #define VP_ERR_FAB_NORESOURCES 3 #define VP_ERR_FAB_LOGOUT 4 #define VP_ERR_ADAP_NORESOURCES 5 - uint16_t max_npiv_vports; /* 63 or 125 per topoloty */ - int cur_vport_count; + int fw_heartbeat_counter; + int seconds_since_last_heartbeat; + struct fc_host_statistics fc_host_stat; + struct qla_statistics qla_stats; - /* Pass through support */ - int pass_thru_cmd_result; - int pass_thru_cmd_in_process; + uint16_t vp_idx; /* vport ID */ - struct qla_chip_state_84xx *cs84xx; + struct req_que *req; + struct qla_hw_data *hw; + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + /* + * Processing Q2T tgt reference. NULL on not enabled targets. Protected + * by tgt_mutex AND hardware_lock for writing and tgt_mutex OR + * hardware_lock for reading. + */ + struct q2t_tgt *tgt; + + uint16_t current_handle; + struct q2t_cmd *cmds[MAX_OUTSTANDING_COMMANDS]; + + struct mutex tgt_mutex; + struct mutex tgt_host_action_mutex; + + /* + * Main Q2T tgt reference, which always points to the target, if the + * target mode addon loaded. Protected by tgt_host_action_mutex. + */ + struct q2t_tgt *q2t_tgt; + + unsigned int ini_mode_force_reverse :1; + unsigned int node_name_set :1; + unsigned int port_name_set :1; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + + atomic_t vref_count; } scsi_qla_host_t; #ifdef CONFIG_SCSI_QLA2XXX_TARGET @@ -2757,7 +3107,7 @@ struct qla_tgt_vp_map { uint8_t idx; scsi_qla_host_t *vha; }; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ /* * Macros to help code, maintain, etc. @@ -2767,10 +3117,20 @@ struct qla_tgt_vp_map { test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || \ atomic_read(&ha->loop_state) == LOOP_DOWN) -#define to_qla_host(x) ((scsi_qla_host_t *) (x)->hostdata) +#define QLA_VHA_MARK_BUSY(__vha, __bail) do { \ + atomic_inc(&__vha->vref_count); \ + mb(); \ + if (__vha->flags.delete_progress) { \ + atomic_dec(&__vha->vref_count); \ + __bail = 1; \ + } else { \ + __bail = 0; \ + } \ +} while (0) -#define qla_printk(level, ha, format, arg...) \ - dev_printk(level , &((ha)->pdev->dev) , format , ## arg) +#define QLA_VHA_MARK_NOT_BUSY(__vha) do { \ + atomic_dec(&__vha->vref_count); \ +} while (0) /* * qla2x00 local function return status codes @@ -2796,7 +3156,6 @@ struct qla_tgt_vp_map { #define QLA_ABORTED 0x105 #define QLA_SUSPENDED 0x106 #define QLA_BUSY 0x107 -#define QLA_RSCNS_HANDLED 0x108 #define QLA_ALREADY_REGISTERED 0x109 #define QLA_FW_NOT_READY 0x10A @@ -2811,16 +3170,26 @@ struct qla_tgt_vp_map { #define OPTROM_SIZE_2322 0x100000 #define OPTROM_SIZE_24XX 0x100000 #define OPTROM_SIZE_25XX 0x200000 +#define OPTROM_SIZE_81XX 0x400000 +#define OPTROM_SIZE_82XX 0x800000 +#define OPTROM_SIZE_83XX 0x1000000 + +#define OPTROM_BURST_SIZE 0x1000 +#define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4) + +#define QLA_DSDS_PER_IOCB 37 + +#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr) + +#define QLA_SG_ALL 1024 + +enum nexus_wait_type { + WAIT_HOST = 0, + WAIT_TARGET, + WAIT_LUN, +}; #include "qla_gbl.h" #include "qla_dbg.h" #include "qla_inline.h" - -#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr) -#define CMD_COMPL_STATUS(Cmnd) ((Cmnd)->SCp.this_residual) -#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual) -#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status) -#define CMD_ACTUAL_SNSLEN(Cmnd) ((Cmnd)->SCp.Message) -#define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in) - -#endif /* __QLA_DEF_H */ +#endif diff --git a/qla2x00t/qla_devtbl.h b/qla2x00t/qla_devtbl.h index d78d35e68..d6ea69df7 100644 --- a/qla2x00t/qla_devtbl.h +++ b/qla2x00t/qla_devtbl.h @@ -72,7 +72,7 @@ static char *qla2x00_model_name[QLA_MODEL_NAMES*2] = { "QLA2462", "Sun PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x141 */ "QLE2460", "Sun PCI-Express to 2Gb FC, Single Channel", /* 0x142 */ "QLE2462", "Sun PCI-Express to 4Gb FC, Single Channel", /* 0x143 */ - "QEM2462" "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */ + "QEM2462", "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */ "QLE2440", "PCI-Express to 4Gb FC, Single Channel", /* 0x145 */ "QLE2464", "PCI-Express to 4Gb FC, Quad Channel", /* 0x146 */ "QLA2440", "PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x147 */ diff --git a/qla2x00t/qla_dfs.c b/qla2x00t/qla_dfs.c index 561a44117..499c74e39 100644 --- a/qla2x00t/qla_dfs.c +++ b/qla2x00t/qla_dfs.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -15,10 +15,11 @@ static atomic_t qla2x00_dfs_root_count; static int qla2x00_dfs_fce_show(struct seq_file *s, void *unused) { - scsi_qla_host_t *ha = s->private; + scsi_qla_host_t *vha = s->private; uint32_t cnt; uint32_t *fce; uint64_t fce_start; + struct qla_hw_data *ha = vha->hw; mutex_lock(&ha->fce_mutex); @@ -51,7 +52,8 @@ qla2x00_dfs_fce_show(struct seq_file *s, void *unused) static int qla2x00_dfs_fce_open(struct inode *inode, struct file *file) { - scsi_qla_host_t *ha = inode->i_private; + scsi_qla_host_t *vha = inode->i_private; + struct qla_hw_data *ha = vha->hw; int rval; if (!ha->flags.fce_enabled) @@ -60,22 +62,23 @@ qla2x00_dfs_fce_open(struct inode *inode, struct file *file) mutex_lock(&ha->fce_mutex); /* Pause tracing to flush FCE buffers. */ - rval = qla2x00_disable_fce_trace(ha, &ha->fce_wr, &ha->fce_rd); + rval = qla2x00_disable_fce_trace(vha, &ha->fce_wr, &ha->fce_rd); if (rval) - qla_printk(KERN_WARNING, ha, + ql_dbg(ql_dbg_user, vha, 0x705c, "DebugFS: Unable to disable FCE (%d).\n", rval); ha->flags.fce_enabled = 0; mutex_unlock(&ha->fce_mutex); out: - return single_open(file, qla2x00_dfs_fce_show, ha); + return single_open(file, qla2x00_dfs_fce_show, vha); } static int qla2x00_dfs_fce_release(struct inode *inode, struct file *file) { - scsi_qla_host_t *ha = inode->i_private; + scsi_qla_host_t *vha = inode->i_private; + struct qla_hw_data *ha = vha->hw; int rval; if (ha->flags.fce_enabled) @@ -86,10 +89,10 @@ qla2x00_dfs_fce_release(struct inode *inode, struct file *file) /* Re-enable FCE tracing. */ ha->flags.fce_enabled = 1; memset(ha->fce, 0, fce_calc_size(ha->fce_bufs)); - rval = qla2x00_enable_fce_trace(ha, ha->fce_dma, ha->fce_bufs, + rval = qla2x00_enable_fce_trace(vha, ha->fce_dma, ha->fce_bufs, ha->fce_mb, &ha->fce_bufs); if (rval) { - qla_printk(KERN_WARNING, ha, + ql_dbg(ql_dbg_user, vha, 0x700d, "DebugFS: Unable to reinitialize FCE (%d).\n", rval); ha->flags.fce_enabled = 0; } @@ -107,9 +110,11 @@ static const struct file_operations dfs_fce_ops = { }; int -qla2x00_dfs_setup(scsi_qla_host_t *ha) +qla2x00_dfs_setup(scsi_qla_host_t *vha) { - if (!IS_QLA25XX(ha)) + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha)) goto out; if (!ha->fce) goto out; @@ -120,8 +125,8 @@ qla2x00_dfs_setup(scsi_qla_host_t *ha) atomic_set(&qla2x00_dfs_root_count, 0); qla2x00_dfs_root = debugfs_create_dir(QLA2XXX_DRIVER_NAME, NULL); if (!qla2x00_dfs_root) { - qla_printk(KERN_NOTICE, ha, - "DebugFS: Unable to create root directory.\n"); + ql_log(ql_log_warn, vha, 0x00f7, + "Unable to create debugfs root directory.\n"); goto out; } @@ -130,21 +135,21 @@ create_dir: goto create_nodes; mutex_init(&ha->fce_mutex); - ha->dfs_dir = debugfs_create_dir(ha->host_str, qla2x00_dfs_root); + ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root); if (!ha->dfs_dir) { - qla_printk(KERN_NOTICE, ha, - "DebugFS: Unable to create ha directory.\n"); + ql_log(ql_log_warn, vha, 0x00f8, + "Unable to create debugfs ha directory.\n"); goto out; } atomic_inc(&qla2x00_dfs_root_count); create_nodes: - ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, ha, + ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, vha, &dfs_fce_ops); if (!ha->dfs_fce) { - qla_printk(KERN_NOTICE, ha, - "DebugFS: Unable to fce node.\n"); + ql_log(ql_log_warn, vha, 0x00f9, + "Unable to create debugfs fce node.\n"); goto out; } out: @@ -152,8 +157,9 @@ out: } int -qla2x00_dfs_remove(scsi_qla_host_t *ha) +qla2x00_dfs_remove(scsi_qla_host_t *vha) { + struct qla_hw_data *ha = vha->hw; if (ha->dfs_fce) { debugfs_remove(ha->dfs_fce); ha->dfs_fce = NULL; diff --git a/qla2x00t/qla_fw.h b/qla2x00t/qla_fw.h index 54289e530..404e3d82e 100644 --- a/qla2x00t/qla_fw.h +++ b/qla2x00t/qla_fw.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -26,6 +26,7 @@ #define PDO_FORCE_ADISC BIT_1 #define PDO_FORCE_PLOGI BIT_0 +#ifdef CONFIG_SCSI_QLA2XXX_TARGET struct qla_port23_data { uint8_t port_name[WWN_SIZE]; uint16_t loop_id; @@ -36,6 +37,7 @@ struct qla_port24_data { uint16_t loop_id; uint16_t reserved; }; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ #define PORT_DATABASE_24XX_SIZE 64 struct port_database_24xx { @@ -309,7 +311,9 @@ struct init_cb_24xx { uint32_t response_q_address[2]; uint32_t prio_request_q_address[2]; - uint8_t reserved_2[8]; + uint16_t msix; + uint16_t msix_atio; + uint8_t reserved_2[4]; uint16_t atio_q_inpointer; uint16_t atio_q_length; @@ -382,8 +386,9 @@ struct init_cb_24xx { * BIT 17-31 = Reserved */ uint32_t firmware_options_3; - - uint8_t reserved_3[24]; + uint16_t qos; + uint16_t rid; + uint8_t reserved_3[20]; }; /* @@ -408,6 +413,7 @@ struct cmd_type_6 { struct scsi_lun lun; /* FCP LUN (BE). */ uint16_t control_flags; /* Control flags. */ +#define CF_DIF_SEG_DESCR_ENABLE BIT_3 #define CF_DATA_SEG_DESCR_ENABLE BIT_2 #define CF_READ_DATA BIT_1 #define CF_WRITE_DATA BIT_0 @@ -423,8 +429,7 @@ struct cmd_type_6 { uint8_t vp_index; uint32_t fcp_data_dseg_address[2]; /* Data segment address. */ - uint16_t fcp_data_dseg_len; /* Data segment length. */ - uint16_t reserved_1; /* MUST be set to 0. */ + uint32_t fcp_data_dseg_len; /* Data segment length. */ }; #define COMMAND_TYPE_7 0x18 /* Command Type 7 entry */ @@ -474,6 +479,43 @@ struct cmd_type_7 { uint32_t dseg_0_len; /* Data segment 0 length. */ }; +#define COMMAND_TYPE_CRC_2 0x6A /* Command Type CRC_2 (Type 6) + * (T10-DIF) */ +struct cmd_type_crc_2 { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + uint16_t nport_handle; /* N_PORT handle. */ + uint16_t timeout; /* Command timeout. */ + + uint16_t dseg_count; /* Data segment count. */ + + uint16_t fcp_rsp_dseg_len; /* FCP_RSP DSD length. */ + + struct scsi_lun lun; /* FCP LUN (BE). */ + + uint16_t control_flags; /* Control flags. */ + + uint16_t fcp_cmnd_dseg_len; /* Data segment length. */ + uint32_t fcp_cmnd_dseg_address[2]; /* Data segment address. */ + + uint32_t fcp_rsp_dseg_address[2]; /* Data segment address. */ + + uint32_t byte_count; /* Total byte count. */ + + uint8_t port_id[3]; /* PortID of destination port. */ + uint8_t vp_index; + + uint32_t crc_context_address[2]; /* Data segment address. */ + uint16_t crc_context_len; /* Data segment length. */ + uint16_t reserved_1; /* MUST be set to 0. */ +}; + + /* * ISP queue - status entry structure definition. */ @@ -504,10 +546,22 @@ struct sts_entry_24xx { uint32_t sense_len; /* FCP SENSE length. */ uint32_t rsp_data_len; /* FCP response data length. */ - uint8_t data[28]; /* FCP response/sense information. */ + /* + * If DIF Error is set in comp_status, these additional fields are + * defined: + * + * !!! NOTE: Firmware sends expected/actual DIF data in big endian + * format; but all of the "data" field gets swab32-d in the beginning + * of qla2x00_status_entry(). + * + * &data[10] : uint8_t report_runt_bg[2]; - computed guard + * &data[12] : uint8_t actual_dif[8]; - DIF Data received + * &data[20] : uint8_t expected_dif[8]; - DIF Data computed + */ }; + /* * Status entry completion status */ @@ -635,6 +689,39 @@ struct els_entry_24xx { uint32_t rx_len; /* Data segment 1 length. */ }; +struct els_sts_entry_24xx { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System Defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + uint16_t comp_status; + + uint16_t nport_handle; /* N_PORT handle. */ + + uint16_t reserved_1; + + uint8_t vp_index; + uint8_t sof_type; + + uint32_t rx_xchg_address; /* Receive exchange address. */ + uint16_t reserved_2; + + uint8_t opcode; + uint8_t reserved_3; + + uint8_t port_id[3]; + uint8_t reserved_4; + + uint16_t reserved_5; + + uint16_t control_flags; /* Control flags. */ + uint32_t total_byte_count; + uint32_t error_subcode_1; + uint32_t error_subcode_2; +}; /* * ISP queue - Mailbox Command entry structure definition. */ @@ -764,7 +851,8 @@ struct abort_entry_24xx { uint32_t handle_to_abort; /* System handle to abort. */ - uint8_t reserved_1[32]; + uint16_t req_que_no; + uint8_t reserved_1[30]; uint8_t port_id[3]; /* PortID of destination port. */ uint8_t vp_index; @@ -799,14 +887,25 @@ struct device_reg_24xx { #define FA_RISC_CODE_ADDR 0x20000 #define FA_RISC_CODE_SEGMENTS 2 +#define FA_FLASH_DESCR_ADDR_24 0x11000 +#define FA_FLASH_LAYOUT_ADDR_24 0x11400 +#define FA_NPIV_CONF0_ADDR_24 0x16000 +#define FA_NPIV_CONF1_ADDR_24 0x17000 + #define FA_FW_AREA_ADDR 0x40000 #define FA_VPD_NVRAM_ADDR 0x48000 #define FA_FEATURE_ADDR 0x4C000 #define FA_FLASH_DESCR_ADDR 0x50000 +#define FA_FLASH_LAYOUT_ADDR 0x50400 #define FA_HW_EVENT0_ADDR 0x54000 -#define FA_HW_EVENT1_ADDR 0x54200 +#define FA_HW_EVENT1_ADDR 0x54400 #define FA_HW_EVENT_SIZE 0x200 #define FA_HW_EVENT_ENTRY_SIZE 4 +#define FA_NPIV_CONF0_ADDR 0x5C000 +#define FA_NPIV_CONF1_ADDR 0x5D000 +#define FA_FCP_PRIO0_ADDR 0x10000 +#define FA_FCP_PRIO1_ADDR 0x12000 + /* * Flash Error Log Event Codes. */ @@ -816,10 +915,6 @@ struct device_reg_24xx { #define HW_EVENT_NVRAM_CHKSUM_ERR 0xF023 #define HW_EVENT_FLASH_FW_ERR 0xF024 -#define FA_BOOT_LOG_ADDR 0x58000 -#define FA_FW_DUMP0_ADDR 0x60000 -#define FA_FW_DUMP1_ADDR 0x70000 - uint32_t flash_data; /* Flash/NVRAM BIOS data. */ uint32_t ctrl_status; /* Control/Status. */ @@ -880,7 +975,6 @@ struct device_reg_24xx { /* HCCR statuses. */ #define HCCRX_HOST_INT BIT_6 /* Host to RISC interrupt bit. */ #define HCCRX_RISC_RESET BIT_5 /* RISC Reset mode bit. */ -#define HCCRX_RISC_PAUSE BIT_4 /* RISC Pause mode bit. */ /* HCCR commands. */ /* NOOP. */ #define HCCRX_NOOP 0x00000000 @@ -993,12 +1087,12 @@ struct device_reg_24xx { #define MIN_MULTI_ID_FABRIC 64 /* Must be power-of-2. */ #define MAX_MULTI_ID_FABRIC 256 /* ... */ -#define for_each_mapped_vp_idx(_ha, _idx) \ - for (_idx = find_next_bit((_ha)->vp_idx_map, \ - (_ha)->max_npiv_vports + 1, 1); \ - _idx <= (_ha)->max_npiv_vports; \ - _idx = find_next_bit((_ha)->vp_idx_map, \ - (_ha)->max_npiv_vports + 1, _idx + 1)) \ +#define for_each_mapped_vp_idx(_vha, _idx) \ + for (_idx = find_next_bit((_vha)->hw->vp_idx_map, \ + (_vha)->hw->max_npiv_vports + 1, 1); \ + _idx <= (_vha)->hw->max_npiv_vports; \ + _idx = find_next_bit((_vha)->hw->vp_idx_map, \ + (_vha)->hw->max_npiv_vports + 1, _idx + 1)) \ struct mid_conf_entry_24xx { uint16_t reserved_1; @@ -1129,7 +1223,7 @@ struct vp_config_entry_24xx { uint16_t id; uint16_t reserved_4; uint16_t hopct; - uint8_t reserved_5; + uint8_t reserved_5[2]; }; #define VP_RPT_ID_IOCB_TYPE 0x32 /* Report ID Acquisition entry. */ @@ -1213,6 +1307,72 @@ struct qla_fdt_layout { uint8_t unused2[65]; }; +/* Flash Layout Table ********************************************************/ + +struct qla_flt_location { + uint8_t sig[4]; + uint16_t start_lo; + uint16_t start_hi; + uint8_t version; + uint8_t unused[5]; + uint16_t checksum; +}; + +struct qla_flt_header { + uint16_t version; + uint16_t length; + uint16_t checksum; + uint16_t unused; +}; + +#define FLT_REG_FW 0x01 +#define FLT_REG_BOOT_CODE 0x07 +#define FLT_REG_VPD_0 0x14 +#define FLT_REG_NVRAM_0 0x15 +#define FLT_REG_VPD_1 0x16 +#define FLT_REG_NVRAM_1 0x17 +#define FLT_REG_FDT 0x1a +#define FLT_REG_FLT 0x1c +#define FLT_REG_HW_EVENT_0 0x1d +#define FLT_REG_HW_EVENT_1 0x1f +#define FLT_REG_NPIV_CONF_0 0x29 +#define FLT_REG_NPIV_CONF_1 0x2a +#define FLT_REG_GOLD_FW 0x2f +#define FLT_REG_FCP_PRIO_0 0x87 +#define FLT_REG_FCP_PRIO_1 0x88 +#define FLT_REG_FCOE_FW 0xA4 +#define FLT_REG_FCOE_VPD_0 0xA9 +#define FLT_REG_FCOE_NVRAM_0 0xAA +#define FLT_REG_FCOE_VPD_1 0xAB +#define FLT_REG_FCOE_NVRAM_1 0xAC + +struct qla_flt_region { + uint32_t code; + uint32_t size; + uint32_t start; + uint32_t end; +}; + +/* Flash NPIV Configuration Table ********************************************/ + +struct qla_npiv_header { + uint8_t sig[2]; + uint16_t version; + uint16_t entries; + uint16_t unused[4]; + uint16_t checksum; +}; + +struct qla_npiv_entry { + uint16_t flags; + uint16_t vf_id; + uint8_t q_qos; + uint8_t f_qos; + uint16_t unused1; + uint8_t port_name[WWN_SIZE]; + uint8_t node_name[WWN_SIZE]; +}; + /* 84XX Support **************************************************************/ #define MBA_ISP84XX_ALERT 0x800f /* Alert Notification. */ @@ -1335,4 +1495,410 @@ struct access_chip_rsp_84xx { uint32_t reserved[12]; }; + +/* 81XX Support **************************************************************/ + +#define MBA_DCBX_START 0x8016 +#define MBA_DCBX_COMPLETE 0x8030 +#define MBA_FCF_CONF_ERR 0x8031 +#define MBA_DCBX_PARAM_UPDATE 0x8032 +#define MBA_IDC_COMPLETE 0x8100 +#define MBA_IDC_NOTIFY 0x8101 +#define MBA_IDC_TIME_EXT 0x8102 + +#define MBC_IDC_ACK 0x101 +#define MBC_RESTART_MPI_FW 0x3d +#define MBC_FLASH_ACCESS_CTRL 0x3e /* Control flash access. */ +#define MBC_GET_XGMAC_STATS 0x7a +#define MBC_GET_DCBX_PARAMS 0x51 + +/* + * ISP83xx mailbox commands + */ +#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */ + +/* Flash access control option field bit definitions */ +#define FAC_OPT_FORCE_SEMAPHORE BIT_15 +#define FAC_OPT_REQUESTOR_ID BIT_14 +#define FAC_OPT_CMD_SUBCODE 0xff + +/* Flash access control command subcodes */ +#define FAC_OPT_CMD_WRITE_PROTECT 0x00 +#define FAC_OPT_CMD_WRITE_ENABLE 0x01 +#define FAC_OPT_CMD_ERASE_SECTOR 0x02 +#define FAC_OPT_CMD_LOCK_SEMAPHORE 0x03 +#define FAC_OPT_CMD_UNLOCK_SEMAPHORE 0x04 +#define FAC_OPT_CMD_GET_SECTOR_SIZE 0x05 + +struct nvram_81xx { + /* NVRAM header. */ + uint8_t id[4]; + uint16_t nvram_version; + uint16_t reserved_0; + + /* Firmware Initialization Control Block. */ + uint16_t version; + uint16_t reserved_1; + uint16_t frame_payload_size; + uint16_t execution_throttle; + uint16_t exchange_count; + uint16_t reserved_2; + + uint8_t port_name[WWN_SIZE]; + uint8_t node_name[WWN_SIZE]; + + uint16_t login_retry_count; + uint16_t reserved_3; + uint16_t interrupt_delay_timer; + uint16_t login_timeout; + + uint32_t firmware_options_1; + uint32_t firmware_options_2; + uint32_t firmware_options_3; + + uint16_t reserved_4[4]; + + /* Offset 64. */ + uint8_t enode_mac[6]; + uint16_t reserved_5[5]; + + /* Offset 80. */ + uint16_t reserved_6[24]; + + /* Offset 128. */ + uint16_t ex_version; + uint8_t prio_fcf_matching_flags; + uint8_t reserved_6_1[3]; + uint16_t pri_fcf_vlan_id; + uint8_t pri_fcf_fabric_name[8]; + uint16_t reserved_6_2[7]; + uint8_t spma_mac_addr[6]; + uint16_t reserved_6_3[14]; + + /* Offset 192. */ + uint16_t reserved_7[32]; + + /* + * BIT 0 = Enable spinup delay + * BIT 1 = Disable BIOS + * BIT 2 = Enable Memory Map BIOS + * BIT 3 = Enable Selectable Boot + * BIT 4 = Disable RISC code load + * BIT 5 = Disable Serdes + * BIT 6 = Opt boot mode + * BIT 7 = Interrupt enable + * + * BIT 8 = EV Control enable + * BIT 9 = Enable lip reset + * BIT 10 = Enable lip full login + * BIT 11 = Enable target reset + * BIT 12 = Stop firmware + * BIT 13 = Enable nodename option + * BIT 14 = Default WWPN valid + * BIT 15 = Enable alternate WWN + * + * BIT 16 = CLP LUN string + * BIT 17 = CLP Target string + * BIT 18 = CLP BIOS enable string + * BIT 19 = CLP Serdes string + * BIT 20 = CLP WWPN string + * BIT 21 = CLP WWNN string + * BIT 22 = + * BIT 23 = + * BIT 24 = Keep WWPN + * BIT 25 = Temp WWPN + * BIT 26-31 = + */ + uint32_t host_p; + + uint8_t alternate_port_name[WWN_SIZE]; + uint8_t alternate_node_name[WWN_SIZE]; + + uint8_t boot_port_name[WWN_SIZE]; + uint16_t boot_lun_number; + uint16_t reserved_8; + + uint8_t alt1_boot_port_name[WWN_SIZE]; + uint16_t alt1_boot_lun_number; + uint16_t reserved_9; + + uint8_t alt2_boot_port_name[WWN_SIZE]; + uint16_t alt2_boot_lun_number; + uint16_t reserved_10; + + uint8_t alt3_boot_port_name[WWN_SIZE]; + uint16_t alt3_boot_lun_number; + uint16_t reserved_11; + + /* + * BIT 0 = Selective Login + * BIT 1 = Alt-Boot Enable + * BIT 2 = Reserved + * BIT 3 = Boot Order List + * BIT 4 = Reserved + * BIT 5 = Selective LUN + * BIT 6 = Reserved + * BIT 7-31 = + */ + uint32_t efi_parameters; + + uint8_t reset_delay; + uint8_t reserved_12; + uint16_t reserved_13; + + uint16_t boot_id_number; + uint16_t reserved_14; + + uint16_t max_luns_per_target; + uint16_t reserved_15; + + uint16_t port_down_retry_count; + uint16_t link_down_timeout; + + /* FCode parameters. */ + uint16_t fcode_parameter; + + uint16_t reserved_16[3]; + + /* Offset 352. */ + uint8_t reserved_17[4]; + uint16_t reserved_18[5]; + uint8_t reserved_19[2]; + uint16_t reserved_20[8]; + + /* Offset 384. */ + uint8_t reserved_21[16]; + uint16_t reserved_22[3]; + + /* + * BIT 0 = Extended BB credits for LR + * BIT 1 = Virtual Fabric Enable + * BIT 2 = Enhanced Features Unused + * BIT 3-7 = Enhanced Features Reserved + */ + /* Enhanced Features */ + uint8_t enhanced_features; + + uint8_t reserved_23; + uint16_t reserved_24[4]; + + /* Offset 416. */ + uint16_t reserved_25[32]; + + /* Offset 480. */ + uint8_t model_name[16]; + + /* Offset 496. */ + uint16_t feature_mask_l; + uint16_t feature_mask_h; + uint16_t reserved_26[2]; + + uint16_t subsystem_vendor_id; + uint16_t subsystem_device_id; + + uint32_t checksum; +}; + +/* + * ISP Initialization Control Block. + * Little endian except where noted. + */ +#define ICB_VERSION 1 +struct init_cb_81xx { + uint16_t version; + uint16_t reserved_1; + + uint16_t frame_payload_size; + uint16_t execution_throttle; + uint16_t exchange_count; + + uint16_t reserved_2; + + uint8_t port_name[WWN_SIZE]; /* Big endian. */ + uint8_t node_name[WWN_SIZE]; /* Big endian. */ + + uint16_t response_q_inpointer; + uint16_t request_q_outpointer; + + uint16_t login_retry_count; + + uint16_t prio_request_q_outpointer; + + uint16_t response_q_length; + uint16_t request_q_length; + + uint16_t reserved_3; + + uint16_t prio_request_q_length; + + uint32_t request_q_address[2]; + uint32_t response_q_address[2]; + uint32_t prio_request_q_address[2]; + + uint8_t reserved_4[8]; + + uint16_t atio_q_inpointer; + uint16_t atio_q_length; + uint32_t atio_q_address[2]; + + uint16_t interrupt_delay_timer; /* 100us increments. */ + uint16_t login_timeout; + + /* + * BIT 0-3 = Reserved + * BIT 4 = Enable Target Mode + * BIT 5 = Disable Initiator Mode + * BIT 6 = Reserved + * BIT 7 = Reserved + * + * BIT 8-13 = Reserved + * BIT 14 = Node Name Option + * BIT 15-31 = Reserved + */ + uint32_t firmware_options_1; + + /* + * BIT 0 = Operation Mode bit 0 + * BIT 1 = Operation Mode bit 1 + * BIT 2 = Operation Mode bit 2 + * BIT 3 = Operation Mode bit 3 + * BIT 4-7 = Reserved + * + * BIT 8 = Enable Class 2 + * BIT 9 = Enable ACK0 + * BIT 10 = Reserved + * BIT 11 = Enable FC-SP Security + * BIT 12 = FC Tape Enable + * BIT 13 = Reserved + * BIT 14 = Enable Target PRLI Control + * BIT 15-31 = Reserved + */ + uint32_t firmware_options_2; + + /* + * BIT 0-3 = Reserved + * BIT 4 = FCP RSP Payload bit 0 + * BIT 5 = FCP RSP Payload bit 1 + * BIT 6 = Enable Receive Out-of-Order data frame handling + * BIT 7 = Reserved + * + * BIT 8 = Reserved + * BIT 9 = Enable Out-of-Order FCP_XFER_RDY relative offset handling + * BIT 10-16 = Reserved + * BIT 17 = Enable multiple FCFs + * BIT 18-20 = MAC addressing mode + * BIT 21-25 = Ethernet data rate + * BIT 26 = Enable ethernet header rx IOCB for ATIO q + * BIT 27 = Enable ethernet header rx IOCB for response q + * BIT 28 = SPMA selection bit 0 + * BIT 28 = SPMA selection bit 1 + * BIT 30-31 = Reserved + */ + uint32_t firmware_options_3; + + uint8_t reserved_5[8]; + + uint8_t enode_mac[6]; + + uint8_t reserved_6[10]; +}; + +struct mid_init_cb_81xx { + struct init_cb_81xx init_cb; + + uint16_t count; + uint16_t options; + + struct mid_conf_entry_24xx entries[MAX_MULTI_ID_FABRIC]; +}; + +struct ex_init_cb_81xx { + uint16_t ex_version; + uint8_t prio_fcf_matching_flags; + uint8_t reserved_1[3]; + uint16_t pri_fcf_vlan_id; + uint8_t pri_fcf_fabric_name[8]; + uint16_t reserved_2[7]; + uint8_t spma_mac_addr[6]; + uint16_t reserved_3[14]; +}; + +#define FARX_ACCESS_FLASH_CONF_81XX 0x7FFD0000 +#define FARX_ACCESS_FLASH_DATA_81XX 0x7F800000 + +/* FCP priority config defines *************************************/ +/* operations */ +#define QLFC_FCP_PRIO_DISABLE 0x0 +#define QLFC_FCP_PRIO_ENABLE 0x1 +#define QLFC_FCP_PRIO_GET_CONFIG 0x2 +#define QLFC_FCP_PRIO_SET_CONFIG 0x3 + +struct qla_fcp_prio_entry { + uint16_t flags; /* Describes parameter(s) in FCP */ + /* priority entry that are valid */ +#define FCP_PRIO_ENTRY_VALID 0x1 +#define FCP_PRIO_ENTRY_TAG_VALID 0x2 +#define FCP_PRIO_ENTRY_SPID_VALID 0x4 +#define FCP_PRIO_ENTRY_DPID_VALID 0x8 +#define FCP_PRIO_ENTRY_LUNB_VALID 0x10 +#define FCP_PRIO_ENTRY_LUNE_VALID 0x20 +#define FCP_PRIO_ENTRY_SWWN_VALID 0x40 +#define FCP_PRIO_ENTRY_DWWN_VALID 0x80 + uint8_t tag; /* Priority value */ + uint8_t reserved; /* Reserved for future use */ + uint32_t src_pid; /* Src port id. high order byte */ + /* unused; -1 (wild card) */ + uint32_t dst_pid; /* Src port id. high order byte */ + /* unused; -1 (wild card) */ + uint16_t lun_beg; /* 1st lun num of lun range. */ + /* -1 (wild card) */ + uint16_t lun_end; /* 2nd lun num of lun range. */ + /* -1 (wild card) */ + uint8_t src_wwpn[8]; /* Source WWPN: -1 (wild card) */ + uint8_t dst_wwpn[8]; /* Destination WWPN: -1 (wild card) */ +}; + +struct qla_fcp_prio_cfg { + uint8_t signature[4]; /* "HQOS" signature of config data */ + uint16_t version; /* 1: Initial version */ + uint16_t length; /* config data size in num bytes */ + uint16_t checksum; /* config data bytes checksum */ + uint16_t num_entries; /* Number of entries */ + uint16_t size_of_entry; /* Size of each entry in num bytes */ + uint8_t attributes; /* enable/disable, persistence */ +#define FCP_PRIO_ATTR_DISABLE 0x0 +#define FCP_PRIO_ATTR_ENABLE 0x1 +#define FCP_PRIO_ATTR_PERSIST 0x2 + uint8_t reserved; /* Reserved for future use */ +#define FCP_PRIO_CFG_HDR_SIZE 0x10 + struct qla_fcp_prio_entry entry[1]; /* fcp priority entries */ +#define FCP_PRIO_CFG_ENTRY_SIZE 0x20 +}; + +#define FCP_PRIO_CFG_SIZE (32*1024) /* fcp prio data per port*/ + +/* 25XX Support ****************************************************/ +#define FA_FCP_PRIO0_ADDR_25 0x3C000 +#define FA_FCP_PRIO1_ADDR_25 0x3E000 + +/* 81XX Flash locations -- occupies second 2MB region. */ +#define FA_BOOT_CODE_ADDR_81 0x80000 +#define FA_RISC_CODE_ADDR_81 0xA0000 +#define FA_FW_AREA_ADDR_81 0xC0000 +#define FA_VPD_NVRAM_ADDR_81 0xD0000 +#define FA_VPD0_ADDR_81 0xD0000 +#define FA_VPD1_ADDR_81 0xD0400 +#define FA_NVRAM0_ADDR_81 0xD0080 +#define FA_NVRAM1_ADDR_81 0xD0180 +#define FA_FEATURE_ADDR_81 0xD4000 +#define FA_FLASH_DESCR_ADDR_81 0xD8000 +#define FA_FLASH_LAYOUT_ADDR_81 0xD8400 +#define FA_HW_EVENT0_ADDR_81 0xDC000 +#define FA_HW_EVENT1_ADDR_81 0xDC400 +#define FA_NPIV_CONF0_ADDR_81 0xD1000 +#define FA_NPIV_CONF1_ADDR_81 0xD2000 + +/* 83XX Flash locations -- occupies second 8MB region. */ +#define FA_FLASH_LAYOUT_ADDR_83 0xFC400 + #endif diff --git a/qla2x00t/qla_gbl.h b/qla2x00t/qla_gbl.h index a8a67be21..c83cfc393 100644 --- a/qla2x00t/qla_gbl.h +++ b/qla2x00t/qla_gbl.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -28,19 +28,25 @@ extern void qla2x00_reset_adapter(struct scsi_qla_host *); extern void qla24xx_reset_adapter(struct scsi_qla_host *); extern int qla2x00_nvram_config(struct scsi_qla_host *); extern int qla24xx_nvram_config(struct scsi_qla_host *); +extern int qla81xx_nvram_config(struct scsi_qla_host *); extern void qla2x00_update_fw_options(struct scsi_qla_host *); extern void qla24xx_update_fw_options(scsi_qla_host_t *); +extern void qla81xx_update_fw_options(scsi_qla_host_t *); extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *); extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *); +extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *); +extern int qla2x00_perform_loop_resync(scsi_qla_host_t *); extern int qla2x00_loop_resync(scsi_qla_host_t *); - +extern int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *); extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *); extern int qla2x00_local_device_login(scsi_qla_host_t *, fc_port_t *); extern void qla2x00_update_fcports(scsi_qla_host_t *); extern int qla2x00_abort_isp(scsi_qla_host_t *); +extern void qla2x00_abort_isp_cleanup(scsi_qla_host_t *); +extern void qla82xx_quiescent_state_cleanup(scsi_qla_host_t *); extern void qla2x00_update_fcport(scsi_qla_host_t *, fc_port_t *); @@ -51,13 +57,29 @@ extern void qla2x00_enable_tgt_mode(scsi_qla_host_t *ha); extern void qla2x00_disable_tgt_mode(scsi_qla_host_t *ha); extern int qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked); +extern int qla2x00_get_thermal_temp(scsi_qla_host_t *, uint16_t *, uint16_t *); extern void qla84xx_put_chip(struct scsi_qla_host *); extern int qla2x00_configure_loop(scsi_qla_host_t *); extern int qla2x00_configure_local_loop(scsi_qla_host_t *); extern int qla2x00_configure_fabric(scsi_qla_host_t *); +extern int qla2x00_async_login(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern int qla2x00_async_logout(struct scsi_qla_host *, fc_port_t *); +extern int qla2x00_async_adisc(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern int qla2x00_async_tm_cmd(fc_port_t *, uint32_t, uint32_t, uint32_t); +extern void qla2x00_async_login_done(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern void qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern void qla2x00_async_adisc_done(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern void *qla2x00_alloc_iocbs(struct scsi_qla_host *, srb_t *); +extern fc_port_t * +qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t ); /* * Global Data in qla_os.c source file. */ @@ -70,41 +92,75 @@ extern int ql2xloginretrycount; extern int ql2xfdmienable; extern int ql2xallocfwdump; extern int ql2xextended_error_logging; -extern int ql2xqfullrampup; +extern int ql2xiidmaenable; +extern int ql2xmaxqueues; +extern int ql2xmultique_tag; +extern int ql2xfwloadbin; +extern int ql2xetsenable; +extern int ql2xshiftctondsd; +extern int ql2xdbwr; +extern int ql2xdontresethba; +extern unsigned int ql2xmaxlun; +extern int ql2xasynctmfenable; +extern int ql2xenabledif; +extern int ql2xenablehba_err_chk; +extern int ql2xtargetreset; +extern int ql2xgffidenable; +extern int ql2xmdcapmask; +extern int ql2xmdenable; extern int num_hosts; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum fc_host_event_code, u32); -extern int qla2x00_post_hwe_work(struct scsi_qla_host *, uint16_t , uint16_t, - uint16_t, uint16_t); +extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *); +extern int qla2x00_post_async_login_work(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern int qla2x00_post_async_login_done_work(struct scsi_qla_host *, + fc_port_t *, uint16_t *); +extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *, + fc_port_t *, uint16_t *); +extern int qla2x00_post_async_adisc_work(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern int qla2x00_post_async_adisc_done_work(struct scsi_qla_host *, + fc_port_t *, uint16_t *); +extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); -extern void qla2x00_abort_fcport_cmds(fc_port_t *); +extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *); + +extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *, + struct qla_hw_data *); +extern void qla2x00_free_host(struct scsi_qla_host *); +extern void qla2x00_relogin(struct scsi_qla_host *); +extern void qla2x00_do_work(struct scsi_qla_host *); +extern void qla2x00_free_fcports(struct scsi_qla_host *); /* * Global Functions in qla_mid.c source file. */ -extern struct scsi_host_template qla24xx_driver_template; +extern struct scsi_host_template qla2xxx_driver_template; extern struct scsi_transport_template *qla2xxx_transport_vport_template; extern void qla2x00_timer(scsi_qla_host_t *); extern void qla2x00_start_timer(scsi_qla_host_t *, void *, unsigned long); extern void qla24xx_deallocate_vp_id(scsi_qla_host_t *); -extern int qla24xx_disable_vp (scsi_qla_host_t *); -extern int qla24xx_enable_vp (scsi_qla_host_t *); +extern int qla24xx_init_vp(scsi_qla_host_t *); +extern int qla24xx_disable_vp(scsi_qla_host_t *); +extern int qla24xx_enable_vp(scsi_qla_host_t *); extern int qla24xx_control_vp(scsi_qla_host_t *, int ); extern int qla24xx_modify_vp_config(scsi_qla_host_t *); extern int qla2x00_send_change_request(scsi_qla_host_t *, uint16_t, uint16_t); extern void qla2x00_vp_stop_timer(scsi_qla_host_t *); -extern int qla24xx_configure_vhba (scsi_qla_host_t *); +extern int qla24xx_configure_vhba(scsi_qla_host_t *); extern void qla24xx_report_id_acquisition(scsi_qla_host_t *, struct vp_rpt_id_entry_24xx *); extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *); extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *); -extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *); - -extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *); +extern scsi_qla_host_t *qla24xx_create_vhost(struct fc_vport *); +extern void qla2x00_sp_free_dma(void *, void *); extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *); extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int, int); @@ -113,25 +169,33 @@ extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *, int); extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *); extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *); +extern int qla2x00_wait_for_chip_reset(scsi_qla_host_t *); +extern int qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *); -extern void qla2xxx_wake_dpc(scsi_qla_host_t *); -extern void qla2x00_alert_all_vps(scsi_qla_host_t *, uint16_t *); -extern void qla2x00_async_event(scsi_qla_host_t *, uint16_t *); -extern void qla2x00_vp_abort_isp(scsi_qla_host_t *); +extern void qla2xxx_wake_dpc(struct scsi_qla_host *); +extern void qla2x00_alert_all_vps(struct rsp_que *, uint16_t *); +extern void qla2x00_async_event(scsi_qla_host_t *, struct rsp_que *, + uint16_t *); +extern int qla2x00_vp_abort_isp(scsi_qla_host_t *); /* * Global Function Prototypes in qla_iocb.c source file. */ -extern void qla2x00_isp_cmd(scsi_qla_host_t *); - +extern void qla2x00_start_iocbs(struct scsi_qla_host *, struct req_que *); extern uint16_t qla2x00_calc_iocbs_32(uint16_t); extern uint16_t qla2x00_calc_iocbs_64(uint16_t); extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t); extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t); extern int qla2x00_start_scsi(srb_t *sp); extern int qla24xx_start_scsi(srb_t *sp); -int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); -int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); +extern int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *, + uint16_t, uint16_t, uint8_t); +extern int __qla2x00_marker(struct scsi_qla_host *, struct req_que *, + struct rsp_que *, uint16_t, uint16_t, uint8_t); +extern int qla2x00_start_sp(srb_t *); +extern uint16_t qla24xx_calc_iocbs(scsi_qla_host_t *, uint16_t); +extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t); +extern int qla24xx_dif_start_scsi(srb_t *); /* * Global Function Prototypes in qla_mbx.c source file. @@ -145,9 +209,8 @@ qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); extern int qla2x00_execute_fw(scsi_qla_host_t *, uint32_t); -extern void -qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *, - uint16_t *, uint16_t *, uint16_t *, uint32_t *); +extern int +qla2x00_get_fw_version(scsi_qla_host_t *); extern int qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *); @@ -165,13 +228,13 @@ extern int qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t); extern int -qla2x00_abort_command(scsi_qla_host_t *, srb_t *); +qla2x00_abort_command(srb_t *); extern int -qla2x00_abort_target(struct fc_port *, unsigned int); +qla2x00_abort_target(struct fc_port *, unsigned int, int); extern int -qla2x00_lun_reset(struct fc_port *, unsigned int); +qla2x00_lun_reset(struct fc_port *, unsigned int, int); extern int qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *, @@ -183,9 +246,11 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *); extern int qla2x00_init_firmware(scsi_qla_host_t *, uint16_t); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET extern int -qla2x00_get_node_name_list(scsi_qla_host_t *ha, +qla2x00_get_node_name_list(scsi_qla_host_t *ha, bool include_initiators, void **out_data, int *out_len); +#endif extern int qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t); @@ -227,7 +292,7 @@ qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *); extern int qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *, - uint16_t *, uint16_t *, uint16_t *); + uint16_t *, uint16_t *, uint16_t *, uint16_t *); extern int qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map); @@ -240,10 +305,14 @@ extern int qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *, dma_addr_t); -extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *); -extern int qla24xx_abort_target(struct fc_port *, unsigned int); -extern int qla24xx_lun_reset(struct fc_port *, unsigned int); - +extern int qla24xx_abort_command(srb_t *); +extern int +qla24xx_abort_target(struct fc_port *, unsigned int, int); +extern int +qla24xx_lun_reset(struct fc_port *, unsigned int, int); +extern int +qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *, unsigned int, + unsigned int, enum nexus_wait_type); extern int qla2x00_system_error(scsi_qla_host_t *); @@ -266,25 +335,73 @@ extern int qla2x00_disable_fce_trace(scsi_qla_host_t *, uint64_t *, uint64_t *); extern int -qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t, uint16_t); +qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint8_t *, + uint16_t, uint16_t, uint16_t, uint16_t); + +extern int +qla2x00_write_sfp(scsi_qla_host_t *, dma_addr_t, uint8_t *, + uint16_t, uint16_t, uint16_t, uint16_t); extern int qla2x00_set_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t *); extern int qla84xx_verify_chip(struct scsi_qla_host *, uint16_t *); +extern int qla81xx_idc_ack(scsi_qla_host_t *, uint16_t *); + +extern int +qla81xx_fac_get_sector_size(scsi_qla_host_t *, uint32_t *); + +extern int +qla81xx_fac_do_write_enable(scsi_qla_host_t *, int); + +extern int +qla81xx_fac_erase_sector(scsi_qla_host_t *, uint32_t, uint32_t); + +extern int +qla2x00_get_xgmac_stats(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t *); + +extern int +qla2x00_get_dcbx_params(scsi_qla_host_t *, dma_addr_t, uint16_t); + +extern int +qla2x00_read_ram_word(scsi_qla_host_t *, uint32_t, uint32_t *); + +extern int +qla2x00_write_ram_word(scsi_qla_host_t *, uint32_t, uint32_t); + +extern int +qla81xx_write_mpi_register(scsi_qla_host_t *, uint16_t *); +extern int qla2x00_get_data_rate(scsi_qla_host_t *); +extern int qla24xx_set_fcp_prio(scsi_qla_host_t *, uint16_t, uint16_t, + uint16_t *); +extern int +qla81xx_get_port_config(scsi_qla_host_t *, uint16_t *); + +extern int +qla81xx_set_port_config(scsi_qla_host_t *, uint16_t *); + +extern int +qla2x00_port_logout(scsi_qla_host_t *, struct fc_port *); + /* * Global Function Prototypes in qla_isr.c source file. */ extern irqreturn_t qla2100_intr_handler(int, void *); extern irqreturn_t qla2300_intr_handler(int, void *); extern irqreturn_t qla24xx_intr_handler(int, void *); -extern void qla2x00_process_response_queue(struct scsi_qla_host *); -extern void qla24xx_process_response_queue(struct scsi_qla_host *); - -extern int qla2x00_request_irqs(scsi_qla_host_t *); +extern void qla2x00_process_response_queue(struct rsp_que *); +extern void +qla24xx_process_response_queue(struct scsi_qla_host *, struct rsp_que *); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +extern void qla24xx_process_atio_queue(struct scsi_qla_host *ha); +#endif +extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *); extern void qla2x00_free_irqs(scsi_qla_host_t *); +extern int qla2x00_get_data_rate(scsi_qla_host_t *); +extern char* qla2x00_get_link_speed_str(struct qla_hw_data *); + /* * Global Function Prototypes in qla_sup.c source file. */ @@ -310,6 +427,11 @@ extern void qla2x00_beacon_blink(struct scsi_qla_host *); extern int qla24xx_beacon_on(struct scsi_qla_host *); extern int qla24xx_beacon_off(struct scsi_qla_host *); extern void qla24xx_beacon_blink(struct scsi_qla_host *); +extern void qla83xx_beacon_blink(struct scsi_qla_host *); +extern int qla82xx_beacon_on(struct scsi_qla_host *); +extern int qla82xx_beacon_off(struct scsi_qla_host *); +extern int qla83xx_write_remote_reg(scsi_qla_host_t *ha, uint32_t reg, + uint32_t data); extern uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); @@ -325,12 +447,12 @@ extern uint8_t *qla25xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *); extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); -extern int qla2xxx_hw_event_log(scsi_qla_host_t *, uint16_t , uint16_t, - uint16_t, uint16_t); - -extern void qla2xxx_get_flash_info(scsi_qla_host_t *); +extern int qla2xxx_get_flash_info(scsi_qla_host_t *); +extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t); +extern void qla2xxx_flash_npiv_conf(scsi_qla_host_t *); extern int qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp); +extern int qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *); /* * Global Function Prototypes in qla_dbg.c source file. @@ -339,8 +461,14 @@ extern void qla2100_fw_dump(scsi_qla_host_t *, int); extern void qla2300_fw_dump(scsi_qla_host_t *, int); extern void qla24xx_fw_dump(scsi_qla_host_t *, int); extern void qla25xx_fw_dump(scsi_qla_host_t *, int); +extern void qla81xx_fw_dump(scsi_qla_host_t *, int); extern void qla2x00_dump_regs(scsi_qla_host_t *); extern void qla2x00_dump_buffer(uint8_t *, uint32_t); +extern void qla2x00_dump_buffer_zipped(uint8_t *, uint32_t); +extern void qla2xxx_dump_post_process(scsi_qla_host_t *, int); +extern void ql_dump_regs(uint32_t, scsi_qla_host_t *, int32_t); +extern void ql_dump_buffer(uint32_t, scsi_qla_host_t *, int32_t, + uint8_t *, uint32_t); /* * Global Function Prototypes in qla_gs.c source file. @@ -351,6 +479,7 @@ extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *); extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gnn_id(scsi_qla_host_t *, sw_info_t *); +extern void qla2x00_gff_id(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_rft_id(scsi_qla_host_t *); extern int qla2x00_rff_id(scsi_qla_host_t *); extern int qla2x00_rnn_id(scsi_qla_host_t *); @@ -361,7 +490,6 @@ extern int qla2x00_fdmi_register(scsi_qla_host_t *); extern int qla2x00_gfpn_id(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gpsc(scsi_qla_host_t *, sw_info_t *); extern void qla2x00_get_sym_node_name(scsi_qla_host_t *, uint8_t *); -extern int qla2x00_mgmt_svr_login(scsi_qla_host_t *); /* * Global Function Prototypes in qla_attr.c source file. @@ -376,10 +504,125 @@ extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *); extern void qla2x00_init_host_attr(scsi_qla_host_t *); extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *); extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *); +extern int qla2x00_loopback_test(scsi_qla_host_t *, struct msg_echo_lb *, uint16_t *); +extern int qla2x00_echo_test(scsi_qla_host_t *, + struct msg_echo_lb *, uint16_t *); +extern int qla24xx_update_all_fcp_prio(scsi_qla_host_t *); +extern int qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *, + struct qla_fcp_prio_cfg *, uint8_t); /* * Global Function Prototypes in qla_dfs.c source file. */ extern int qla2x00_dfs_setup(scsi_qla_host_t *); extern int qla2x00_dfs_remove(scsi_qla_host_t *); + +/* Globa function prototypes for multi-q */ +extern int qla25xx_request_irq(struct rsp_que *); +extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *); +extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *); +extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t, + uint16_t, int, uint8_t); +extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t, + uint16_t, int); +extern void qla2x00_init_response_q_entries(struct rsp_que *); +extern int qla25xx_delete_req_que(struct scsi_qla_host *, struct req_que *); +extern int qla25xx_delete_queues(struct scsi_qla_host *); +extern uint16_t qla24xx_rd_req_reg(struct qla_hw_data *, uint16_t); +extern uint16_t qla25xx_rd_req_reg(struct qla_hw_data *, uint16_t); +extern void qla24xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t); +extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t); +extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t); +extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t); + +/* qla82xx related functions */ + +/* PCI related functions */ +extern int qla82xx_pci_config(struct scsi_qla_host *); +extern char *qla82xx_pci_info_str(struct scsi_qla_host *, char *, int); +extern int qla82xx_iospace_config(struct qla_hw_data *); + +/* Initialization related functions */ +extern void qla82xx_reset_chip(struct scsi_qla_host *); +extern void qla82xx_config_rings(struct scsi_qla_host *); +extern void qla82xx_watchdog(scsi_qla_host_t *); +extern int qla82xx_start_firmware(scsi_qla_host_t *); +extern void qla2x00_set_model_info(scsi_qla_host_t *, uint8_t *, + size_t, char *); + +/* Firmware and flash related functions */ +extern int qla82xx_load_risc(scsi_qla_host_t *, uint32_t *); +extern uint8_t *qla82xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, + uint32_t, uint32_t); +extern int qla82xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, + uint32_t, uint32_t); + +/* Mailbox related functions */ +extern int qla82xx_abort_isp(scsi_qla_host_t *); +extern int qla82xx_restart_isp(scsi_qla_host_t *); +extern int qla82xx_mbx_intr_enable(scsi_qla_host_t *); +extern int qla82xx_mbx_intr_disable(scsi_qla_host_t *); + +/* IOCB related functions */ +extern int qla82xx_start_scsi(srb_t *); +extern void qla82xx_start_iocbs(scsi_qla_host_t *); +extern void qla2x00_sp_free(void *, void *); +extern void qla2x00_sp_timeout(unsigned long); +extern void qla2x00_bsg_job_done(void *, void *, int); +extern void qla2x00_bsg_sp_free(void *, void *); + +/* Interrupt related */ +extern irqreturn_t qla82xx_intr_handler(int, void *); +extern irqreturn_t qla82xx_msix_default(int, void *); +extern irqreturn_t qla82xx_msix_rsp_q(int, void *); +extern void qla82xx_enable_intrs(struct qla_hw_data *); +extern void qla82xx_disable_intrs(struct qla_hw_data *); +extern void qla82xx_poll(int, void *); +extern void qla82xx_init_flags(struct qla_hw_data *); + +/* ISP 8021 hardware related */ +extern void qla82xx_set_drv_active(scsi_qla_host_t *); +extern int qla82xx_wr_32(struct qla_hw_data *, ulong, u32); +extern int qla82xx_rd_32(struct qla_hw_data *, ulong); + +/* ISP 8021 IDC */ +extern void qla82xx_clear_drv_active(struct qla_hw_data *); +extern uint32_t qla82xx_wait_for_state_change(scsi_qla_host_t *, uint32_t); +extern int qla82xx_idc_lock(struct qla_hw_data *); +extern void qla82xx_idc_unlock(struct qla_hw_data *); +extern int qla82xx_device_state_handler(scsi_qla_host_t *); +extern void qla82xx_clear_qsnt_ready(scsi_qla_host_t *); + +/* Reset related */ +extern int qla82xx_fcoe_ctx_reset(scsi_qla_host_t *); +extern int qla82xx_check_md_needed(scsi_qla_host_t *); +extern void qla82xx_chip_reset_cleanup(scsi_qla_host_t *); +extern int qla81xx_set_led_config(scsi_qla_host_t *, uint16_t *); +extern int qla81xx_get_led_config(scsi_qla_host_t *, uint16_t *); +extern char *qdev_state(uint32_t); +extern int qla82xx_mbx_beacon_ctl(scsi_qla_host_t *, int); +extern void qla82xx_clear_pending_mbx(scsi_qla_host_t *); + +/* BSG related functions */ +extern int qla24xx_bsg_request(struct fc_bsg_job *); +extern int qla24xx_bsg_timeout(struct fc_bsg_job *); +extern int qla84xx_reset_chip(scsi_qla_host_t *, uint16_t); +extern int qla2x00_issue_iocb_timeout(scsi_qla_host_t *, void *, + dma_addr_t, size_t, uint32_t); +extern int qla2x00_get_idma_speed(scsi_qla_host_t *, uint16_t, + uint16_t *, uint16_t *); + +/* 83xx related functions */ +extern void qla83xx_fw_dump(scsi_qla_host_t *, int); +extern int qla83xx_configure_vfs(scsi_qla_host_t *); + +/* Minidump related functions */ +extern int qla82xx_md_get_template_size(scsi_qla_host_t *); +extern int qla82xx_md_get_template(scsi_qla_host_t *); +extern int qla82xx_md_alloc(scsi_qla_host_t *); +extern void qla82xx_md_free(scsi_qla_host_t *); +extern int qla82xx_md_collect(scsi_qla_host_t *); +extern void qla82xx_md_prep(scsi_qla_host_t *); +extern void qla82xx_set_reset_owner(scsi_qla_host_t *); + #endif /* _QLA_GBL_H */ diff --git a/qla2x00t/qla_gs.c b/qla2x00t/qla_gs.c index f8a8eaa83..5f3f3b0b2 100644 --- a/qla2x00t/qla_gs.c +++ b/qla2x00t/qla_gs.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -23,8 +23,9 @@ static int qla2x00_sns_rnn_id(scsi_qla_host_t *); * Returns a pointer to the @ha's ms_iocb. */ void * -qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size) +qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) { + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; ms_pkt = ha->ms_iocb; @@ -60,8 +61,9 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size) * Returns a pointer to the @ha's ms_iocb. */ void * -qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size) +qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) { + struct qla_hw_data *ha = vha->hw; struct ct_entry_24xx *ct_pkt; ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; @@ -83,23 +85,25 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size) ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; - ct_pkt->vp_index = ha->vp_idx; + ct_pkt->vp_index = vha->vp_idx; return (ct_pkt); } /** * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query. - * @ct_req: CT request buffer + * @ct_pkt: CT request and response buffer * @cmd: GS command * @rsp_size: response size in bytes * * Returns a pointer to the intitialized @ct_req. */ static inline struct ct_sns_req * -qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size) +qla2x00_prep_ct_req(struct ct_sns_pkt *ct_pkt, uint16_t cmd, uint16_t rsp_size) { - memset(ct_req, 0, sizeof(struct ct_sns_pkt)); + struct ct_sns_req *ct_req = &ct_pkt->p.req; + + memset(ct_pkt, 0, sizeof(struct ct_sns_pkt)); ct_req->header.revision = 0x01; ct_req->header.gs_type = 0xFC; @@ -111,16 +115,19 @@ qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size) } static int -qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt, +qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, struct ct_sns_rsp *ct_rsp, const char *routine) { int rval; uint16_t comp_status; + struct qla_hw_data *ha = vha->hw; rval = QLA_FUNCTION_FAILED; if (ms_pkt->entry_status != 0) { - DEBUG2_3(printk("scsi(%ld): %s failed, error status (%x).\n", - ha->host_no, routine, ms_pkt->entry_status)); + ql_dbg(ql_dbg_disc, vha, 0x2031, + "%s failed, error status (%x) on port_id: %02x%02x%02x.\n", + routine, ms_pkt->entry_status, vha->d_id.b.domain, + vha->d_id.b.area, vha->d_id.b.al_pa); } else { if (IS_FWI2_CAPABLE(ha)) comp_status = le16_to_cpu( @@ -133,20 +140,24 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt, case CS_DATA_OVERRUN: /* Overrun? */ if (ct_rsp->header.response != __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) { - DEBUG2_3(printk("scsi(%ld): %s failed, " - "rejected request:\n", ha->host_no, - routine)); - DEBUG2_3(qla2x00_dump_buffer( - (uint8_t *)&ct_rsp->header, - sizeof(struct ct_rsp_hdr))); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2077, + "%s failed rejected request on port_id: " + "%02x%02x%02x.\n", routine, + vha->d_id.b.domain, vha->d_id.b.area, + vha->d_id.b.al_pa); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, + 0x2078, (uint8_t *)&ct_rsp->header, + sizeof(struct ct_rsp_hdr)); rval = QLA_INVALID_COMMAND; } else rval = QLA_SUCCESS; break; default: - DEBUG2_3(printk("scsi(%ld): %s failed, completion " - "status (%x).\n", ha->host_no, routine, - comp_status)); + ql_dbg(ql_dbg_disc, vha, 0x2033, + "%s failed, completion status (%x) on port_id: " + "%02x%02x%02x.\n", routine, comp_status, + vha->d_id.b.domain, vha->d_id.b.area, + vha->d_id.b.al_pa); break; } } @@ -161,26 +172,25 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt, * Returns 0 on success. */ int -qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) +qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) { int rval; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct qla_hw_data *ha = vha->hw; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - return (qla2x00_sns_ga_nxt(ha, fcport)); - } + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return qla2x00_sns_ga_nxt(vha, fcport); /* Issue GA_NXT */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GA_NXT_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GA_NXT_REQ_SIZE, GA_NXT_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GA_NXT_CMD, - GA_NXT_RSP_SIZE); + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GA_NXT_CMD, GA_NXT_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_id */ @@ -189,13 +199,13 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa; /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GA_NXT issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "GA_NXT") != + ql_dbg(ql_dbg_disc, vha, 0x2062, + "GA_NXT issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GA_NXT") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { @@ -213,11 +223,10 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) ct_rsp->rsp.ga_nxt.port_type != NS_NL_PORT_TYPE) fcport->d_id.b.domain = 0xf0; - DEBUG2_3(printk("scsi(%ld): GA_NXT entry - " - "nn %02x%02x%02x%02x%02x%02x%02x%02x " + ql_dbg(ql_dbg_disc, vha, 0x2063, + "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x " "pn %02x%02x%02x%02x%02x%02x%02x%02x " - "portid=%02x%02x%02x.\n", - ha->host_no, + "port_id=%02x%02x%02x.\n", fcport->node_name[0], fcport->node_name[1], fcport->node_name[2], fcport->node_name[3], fcport->node_name[4], fcport->node_name[5], @@ -227,12 +236,18 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) fcport->port_name[4], fcport->port_name[5], fcport->port_name[6], fcport->port_name[7], fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); + fcport->d_id.b.al_pa); } return (rval); } +static inline int +qla2x00_gid_pt_rsp_size(scsi_qla_host_t *vha) +{ + return vha->hw->max_fibre_devices * 4 + 16; +} + /** * qla2x00_gid_pt() - SNS scan for fabric devices via GID_PT command. * @ha: HA context @@ -243,7 +258,7 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) * Returns 0 on success. */ int -qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) { int rval; uint16_t i; @@ -253,39 +268,39 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) struct ct_sns_rsp *ct_rsp; struct ct_sns_gid_pt_data *gid_data; + struct qla_hw_data *ha = vha->hw; + uint16_t gid_pt_rsp_size; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - return (qla2x00_sns_gid_pt(ha, list)); - } + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return qla2x00_sns_gid_pt(vha, list); gid_data = NULL; - + gid_pt_rsp_size = qla2x00_gid_pt_rsp_size(vha); /* Issue GID_PT */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GID_PT_REQ_SIZE, - GID_PT_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GID_PT_REQ_SIZE, + gid_pt_rsp_size); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GID_PT_CMD, - GID_PT_RSP_SIZE); + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GID_PT_CMD, gid_pt_rsp_size); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_type */ ct_req->req.gid_pt.port_type = NS_NX_PORT_TYPE; /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GID_PT issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "GID_PT") != + ql_dbg(ql_dbg_disc, vha, 0x2055, + "GID_PT issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GID_PT") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { /* Set port IDs in switch info list. */ - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { gid_data = &ct_rsp->rsp.gid_pt.entries[i]; list[i].d_id.b.domain = gid_data->port_id[0]; list[i].d_id.b.area = gid_data->port_id[1]; @@ -306,7 +321,7 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) * single call. Return a failed status, and let GA_NXT handle * the overload. */ - if (i == MAX_FIBRE_DEVICES) + if (i == ha->max_fibre_devices) rval = QLA_FUNCTION_FAILED; } @@ -321,27 +336,27 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) * Returns 0 on success. */ int -qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; uint16_t i; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct qla_hw_data *ha = vha->hw; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - return (qla2x00_sns_gpn_id(ha, list)); - } + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return qla2x00_sns_gpn_id(vha, list); - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GPN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GPN_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GPN_ID_REQ_SIZE, GPN_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GPN_ID_CMD, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GPN_ID_CMD, GPN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -351,15 +366,17 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list) ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa; /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GPN_ID issue IOCB failed " - "(%d).\n", ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, + ql_dbg(ql_dbg_disc, vha, 0x2056, + "GPN_ID issue IOCB failed (%d).\n", rval); + break; + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GPN_ID") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; + break; } else { /* Save portname */ memcpy(list[i].port_name, @@ -382,27 +399,26 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list) * Returns 0 on success. */ int -qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; uint16_t i; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - return (qla2x00_sns_gnn_id(ha, list)); - } + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return qla2x00_sns_gnn_id(vha, list); - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GNN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GNN_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GNN_ID_REQ_SIZE, GNN_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GNN_ID_CMD, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GNN_ID_CMD, GNN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -412,25 +428,26 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa; /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GNN_ID issue IOCB failed " - "(%d).\n", ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, + ql_dbg(ql_dbg_disc, vha, 0x2057, + "GNN_ID issue IOCB failed (%d).\n", rval); + break; + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GNN_ID") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; + break; } else { /* Save nodename */ memcpy(list[i].node_name, ct_rsp->rsp.gnn_id.node_name, WWN_SIZE); - DEBUG2_3(printk("scsi(%ld): GID_PT entry - " - "nn %02x%02x%02x%02x%02x%02x%02x%02x " - "pn %02x%02x%02x%02x%02x%02x%02x%02x " + ql_dbg(ql_dbg_disc, vha, 0x2058, + "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02X%02x " + "pn %02x%02x%02x%02x%02x%02x%02X%02x " "portid=%02x%02x%02x.\n", - ha->host_no, list[i].node_name[0], list[i].node_name[1], list[i].node_name[2], list[i].node_name[3], list[i].node_name[4], list[i].node_name[5], @@ -440,7 +457,7 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) list[i].port_name[4], list[i].port_name[5], list[i].port_name[6], list[i].port_name[7], list[i].d_id.b.domain, list[i].d_id.b.area, - list[i].d_id.b.al_pa)); + list[i].d_id.b.al_pa); } /* Last device exit. */ @@ -458,48 +475,46 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) * Returns 0 on success. */ int -qla2x00_rft_id(scsi_qla_host_t *ha) +qla2x00_rft_id(scsi_qla_host_t *vha) { int rval; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - return (qla2x00_sns_rft_id(ha)); - } + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return qla2x00_sns_rft_id(vha); /* Issue RFT_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFT_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFT_ID_REQ_SIZE, RFT_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFT_ID_CMD, - RFT_ID_RSP_SIZE); + ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFT_ID_CMD, RFT_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_id, FC-4 types */ - ct_req->req.rft_id.port_id[0] = ha->d_id.b.domain; - ct_req->req.rft_id.port_id[1] = ha->d_id.b.area; - ct_req->req.rft_id.port_id[2] = ha->d_id.b.al_pa; + ct_req->req.rft_id.port_id[0] = vha->d_id.b.domain; + ct_req->req.rft_id.port_id[1] = vha->d_id.b.area; + ct_req->req.rft_id.port_id[2] = vha->d_id.b.al_pa; ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */ /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RFT_ID issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RFT_ID") != + ql_dbg(ql_dbg_disc, vha, 0x2043, + "RFT_ID issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFT_ID") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2044, + "RFT_ID exiting normally.\n"); } return (rval); @@ -512,63 +527,61 @@ qla2x00_rft_id(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla2x00_rff_id(scsi_qla_host_t *ha) +qla2x00_rff_id(scsi_qla_host_t *vha) { int rval; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - DEBUG2(printk("scsi(%ld): RFF_ID call unsupported on " - "ISP2100/ISP2200.\n", ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2046, + "RFF_ID call not supported on ISP2100/ISP2200.\n"); return (QLA_SUCCESS); } /* Issue RFF_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFF_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFF_ID_REQ_SIZE, RFF_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFF_ID_CMD, - RFF_ID_RSP_SIZE); + ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFF_ID_CMD, RFF_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */ - ct_req->req.rff_id.port_id[0] = ha->d_id.b.domain; - ct_req->req.rff_id.port_id[1] = ha->d_id.b.area; - ct_req->req.rff_id.port_id[2] = ha->d_id.b.al_pa; + ct_req->req.rff_id.port_id[0] = vha->d_id.b.domain; + ct_req->req.rff_id.port_id[1] = vha->d_id.b.area; + ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa; #ifdef CONFIG_SCSI_QLA2XXX_TARGET /* * FC-4 Feature bit 0 indicates target functionality to the name server. */ - if (qla_tgt_mode_enabled(ha)) { - if (qla_ini_mode_enabled(ha)) + if (qla_tgt_mode_enabled(vha)) { + if (qla_ini_mode_enabled(vha)) ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1; else ct_req->req.rff_id.fc4_feature = BIT_0; - } else if (qla_ini_mode_enabled(ha)) + } else if (qla_ini_mode_enabled(vha)) #endif ct_req->req.rff_id.fc4_feature = BIT_1; - ct_req->req.rff_id.fc4_type = 0x08; /* SCSI - FCP */ /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RFF_ID issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RFF_ID") != + ql_dbg(ql_dbg_disc, vha, 0x2047, + "RFF_ID issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFF_ID") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): RFF_ID exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2048, + "RFF_ID exiting normally.\n"); } return (rval); @@ -581,56 +594,55 @@ qla2x00_rff_id(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla2x00_rnn_id(scsi_qla_host_t *ha) +qla2x00_rnn_id(scsi_qla_host_t *vha) { int rval; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - return (qla2x00_sns_rnn_id(ha)); - } + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return qla2x00_sns_rnn_id(vha); /* Issue RNN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RNN_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RNN_ID_REQ_SIZE, RNN_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RNN_ID_CMD, - RNN_ID_RSP_SIZE); + ct_req = qla2x00_prep_ct_req(ha->ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_id, node_name */ - ct_req->req.rnn_id.port_id[0] = ha->d_id.b.domain; - ct_req->req.rnn_id.port_id[1] = ha->d_id.b.area; - ct_req->req.rnn_id.port_id[2] = ha->d_id.b.al_pa; + ct_req->req.rnn_id.port_id[0] = vha->d_id.b.domain; + ct_req->req.rnn_id.port_id[1] = vha->d_id.b.area; + ct_req->req.rnn_id.port_id[2] = vha->d_id.b.al_pa; - memcpy(ct_req->req.rnn_id.node_name, ha->node_name, WWN_SIZE); + memcpy(ct_req->req.rnn_id.node_name, vha->node_name, WWN_SIZE); /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RNN_ID issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RNN_ID") != + ql_dbg(ql_dbg_disc, vha, 0x204d, + "RNN_ID issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RNN_ID") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x204e, + "RNN_ID exiting normally.\n"); } return (rval); } void -qla2x00_get_sym_node_name(scsi_qla_host_t *ha, uint8_t *snn) +qla2x00_get_sym_node_name(scsi_qla_host_t *vha, uint8_t *snn) { + struct qla_hw_data *ha = vha->hw; sprintf(snn, "%s FW:v%d.%02d.%02d DVR:v%s",ha->model_number, ha->fw_major_version, ha->fw_minor_version, ha->fw_subminor_version, qla2x00_version_str); @@ -643,34 +655,34 @@ qla2x00_get_sym_node_name(scsi_qla_host_t *ha, uint8_t *snn) * Returns 0 on success. */ int -qla2x00_rsnn_nn(scsi_qla_host_t *ha) +qla2x00_rsnn_nn(scsi_qla_host_t *vha) { int rval; + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - DEBUG2(printk("scsi(%ld): RSNN_ID call unsupported on " - "ISP2100/ISP2200.\n", ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2050, + "RSNN_ID call unsupported on ISP2100/ISP2200.\n"); return (QLA_SUCCESS); } /* Issue RSNN_NN */ /* Prepare common MS IOCB */ /* Request size adjusted after CT preparation */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, 0, RSNN_NN_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, 0, RSNN_NN_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD, - RSNN_NN_RSP_SIZE); + ct_req = qla2x00_prep_ct_req(ha->ct_sns, RSNN_NN_CMD, RSNN_NN_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- node_name, symbolic node_name, size */ - memcpy(ct_req->req.rsnn_nn.node_name, ha->node_name, WWN_SIZE); + memcpy(ct_req->req.rsnn_nn.node_name, vha->node_name, WWN_SIZE); /* Prepare the Symbolic Node Name */ - qla2x00_get_sym_node_name(ha, ct_req->req.rsnn_nn.sym_node_name); + qla2x00_get_sym_node_name(vha, ct_req->req.rsnn_nn.sym_node_name); /* Calculate SNN length */ ct_req->req.rsnn_nn.name_len = @@ -682,18 +694,18 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha) ms_pkt->dseg_req_length = ms_pkt->req_bytecount; /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RSNN_NN issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RSNN_NN") != + ql_dbg(ql_dbg_disc, vha, 0x2051, + "RSNN_NN issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RSNN_NN") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): RSNN_NN exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2052, + "RSNN_NN exiting normally.\n"); } return (rval); @@ -709,11 +721,12 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha) * Returns a pointer to the @ha's sns_cmd. */ static inline struct sns_cmd_pkt * -qla2x00_prep_sns_cmd(scsi_qla_host_t *ha, uint16_t cmd, uint16_t scmd_len, +qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len, uint16_t data_size) { uint16_t wc; struct sns_cmd_pkt *sns_cmd; + struct qla_hw_data *ha = vha->hw; sns_cmd = ha->sns_cmd; memset(sns_cmd, 0, sizeof(struct sns_cmd_pkt)); @@ -739,15 +752,15 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *ha, uint16_t cmd, uint16_t scmd_len, * Returns 0 on success. */ static int -qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) +qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) { int rval; - + struct qla_hw_data *ha = vha->hw; struct sns_cmd_pkt *sns_cmd; /* Issue GA_NXT. */ /* Prepare SNS command request. */ - sns_cmd = qla2x00_prep_sns_cmd(ha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN, + sns_cmd = qla2x00_prep_sns_cmd(vha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN, GA_NXT_SNS_DATA_SIZE); /* Prepare SNS command arguments -- port_id. */ @@ -756,17 +769,18 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) sns_cmd->p.cmd.param[2] = fcport->d_id.b.domain; /* Execute SNS command. */ - rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2, + rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GA_NXT Send SNS failed (%d).\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x205f, + "GA_NXT Send SNS failed (%d).\n", rval); } else if (sns_cmd->p.gan_data[8] != 0x80 || sns_cmd->p.gan_data[9] != 0x02) { - DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, " - "ga_nxt_rsp:\n", ha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gan_data, 16)); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2084, + "GA_NXT failed, rejected request ga_nxt_rsp:\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2074, + sns_cmd->p.gan_data, 16); rval = QLA_FUNCTION_FAILED; } else { /* Populate fc_port_t entry. */ @@ -781,11 +795,10 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) sns_cmd->p.gan_data[16] != NS_NL_PORT_TYPE) fcport->d_id.b.domain = 0xf0; - DEBUG2_3(printk("scsi(%ld): GA_NXT entry - " - "nn %02x%02x%02x%02x%02x%02x%02x%02x " + ql_dbg(ql_dbg_disc, vha, 0x2061, + "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x " "pn %02x%02x%02x%02x%02x%02x%02x%02x " - "portid=%02x%02x%02x.\n", - ha->host_no, + "port_id=%02x%02x%02x.\n", fcport->node_name[0], fcport->node_name[1], fcport->node_name[2], fcport->node_name[3], fcport->node_name[4], fcport->node_name[5], @@ -795,7 +808,7 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) fcport->port_name[4], fcport->port_name[5], fcport->port_name[6], fcport->port_name[7], fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); + fcport->d_id.b.al_pa); } return (rval); @@ -813,38 +826,42 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) * Returns 0 on success. */ static int -qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) { int rval; - + struct qla_hw_data *ha = vha->hw; uint16_t i; uint8_t *entry; struct sns_cmd_pkt *sns_cmd; + uint16_t gid_pt_sns_data_size; + + gid_pt_sns_data_size = qla2x00_gid_pt_rsp_size(vha); /* Issue GID_PT. */ /* Prepare SNS command request. */ - sns_cmd = qla2x00_prep_sns_cmd(ha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN, - GID_PT_SNS_DATA_SIZE); + sns_cmd = qla2x00_prep_sns_cmd(vha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN, + gid_pt_sns_data_size); /* Prepare SNS command arguments -- port_type. */ sns_cmd->p.cmd.param[0] = NS_NX_PORT_TYPE; /* Execute SNS command. */ - rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2, + rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GID_PT Send SNS failed (%d).\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x206d, + "GID_PT Send SNS failed (%d).\n", rval); } else if (sns_cmd->p.gid_data[8] != 0x80 || sns_cmd->p.gid_data[9] != 0x02) { - DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, " - "gid_rsp:\n", ha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gid_data, 16)); + ql_dbg(ql_dbg_disc, vha, 0x202f, + "GID_PT failed, rejected request, gid_rsp:\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2081, + sns_cmd->p.gid_data, 16); rval = QLA_FUNCTION_FAILED; } else { /* Set port IDs in switch info list. */ - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { entry = &sns_cmd->p.gid_data[(i * 4) + 16]; list[i].d_id.b.domain = entry[1]; list[i].d_id.b.area = entry[2]; @@ -863,7 +880,7 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) * single call. Return a failed status, and let GA_NXT handle * the overload. */ - if (i == MAX_FIBRE_DEVICES) + if (i == ha->max_fibre_devices) rval = QLA_FUNCTION_FAILED; } @@ -880,17 +897,17 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) * Returns 0 on success. */ static int -qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; - + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; uint16_t i; struct sns_cmd_pkt *sns_cmd; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GPN_ID */ /* Prepare SNS command request. */ - sns_cmd = qla2x00_prep_sns_cmd(ha, GPN_ID_CMD, + sns_cmd = qla2x00_prep_sns_cmd(vha, GPN_ID_CMD, GPN_ID_SNS_SCMD_LEN, GPN_ID_SNS_DATA_SIZE); /* Prepare SNS command arguments -- port_id. */ @@ -899,17 +916,18 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list) sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain; /* Execute SNS command. */ - rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, + rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GPN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GPN_ID Send SNS failed " - "(%d).\n", ha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x2032, + "GPN_ID Send SNS failed (%d).\n", rval); } else if (sns_cmd->p.gpn_data[8] != 0x80 || sns_cmd->p.gpn_data[9] != 0x02) { - DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected " - "request, gpn_rsp:\n", ha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gpn_data, 16)); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207e, + "GPN_ID failed, rejected request, gpn_rsp:\n"); + ql_dump_buffer(ql_dbg_disc, vha, 0x207f, + sns_cmd->p.gpn_data, 16); rval = QLA_FUNCTION_FAILED; } else { /* Save portname */ @@ -935,17 +953,17 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list) * Returns 0 on success. */ static int -qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; - + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; uint16_t i; struct sns_cmd_pkt *sns_cmd; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GNN_ID */ /* Prepare SNS command request. */ - sns_cmd = qla2x00_prep_sns_cmd(ha, GNN_ID_CMD, + sns_cmd = qla2x00_prep_sns_cmd(vha, GNN_ID_CMD, GNN_ID_SNS_SCMD_LEN, GNN_ID_SNS_DATA_SIZE); /* Prepare SNS command arguments -- port_id. */ @@ -954,28 +972,28 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain; /* Execute SNS command. */ - rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, + rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GNN_ID Send SNS failed " - "(%d).\n", ha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x203f, + "GNN_ID Send SNS failed (%d).\n", rval); } else if (sns_cmd->p.gnn_data[8] != 0x80 || sns_cmd->p.gnn_data[9] != 0x02) { - DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected " - "request, gnn_rsp:\n", ha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gnn_data, 16)); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2082, + "GNN_ID failed, rejected request, gnn_rsp:\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x207a, + sns_cmd->p.gnn_data, 16); rval = QLA_FUNCTION_FAILED; } else { /* Save nodename */ memcpy(list[i].node_name, &sns_cmd->p.gnn_data[16], WWN_SIZE); - DEBUG2_3(printk("scsi(%ld): GID_PT entry - " - "nn %02x%02x%02x%02x%02x%02x%02x%02x " + ql_dbg(ql_dbg_disc, vha, 0x206e, + "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x " "pn %02x%02x%02x%02x%02x%02x%02x%02x " - "portid=%02x%02x%02x.\n", - ha->host_no, + "port_id=%02x%02x%02x.\n", list[i].node_name[0], list[i].node_name[1], list[i].node_name[2], list[i].node_name[3], list[i].node_name[4], list[i].node_name[5], @@ -985,7 +1003,7 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) list[i].port_name[4], list[i].port_name[5], list[i].port_name[6], list[i].port_name[7], list[i].d_id.b.domain, list[i].d_id.b.area, - list[i].d_id.b.al_pa)); + list[i].d_id.b.al_pa); } /* Last device exit. */ @@ -1005,40 +1023,41 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) * Returns 0 on success. */ static int -qla2x00_sns_rft_id(scsi_qla_host_t *ha) +qla2x00_sns_rft_id(scsi_qla_host_t *vha) { int rval; - + struct qla_hw_data *ha = vha->hw; struct sns_cmd_pkt *sns_cmd; /* Issue RFT_ID. */ /* Prepare SNS command request. */ - sns_cmd = qla2x00_prep_sns_cmd(ha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN, + sns_cmd = qla2x00_prep_sns_cmd(vha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN, RFT_ID_SNS_DATA_SIZE); /* Prepare SNS command arguments -- port_id, FC-4 types */ - sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa; - sns_cmd->p.cmd.param[1] = ha->d_id.b.area; - sns_cmd->p.cmd.param[2] = ha->d_id.b.domain; + sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa; + sns_cmd->p.cmd.param[1] = vha->d_id.b.area; + sns_cmd->p.cmd.param[2] = vha->d_id.b.domain; sns_cmd->p.cmd.param[5] = 0x01; /* FCP-3 */ /* Execute SNS command. */ - rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2, + rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RFT_ID Send SNS failed (%d).\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x2060, + "RFT_ID Send SNS failed (%d).\n", rval); } else if (sns_cmd->p.rft_data[8] != 0x80 || sns_cmd->p.rft_data[9] != 0x02) { - DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected request, " - "rft_rsp:\n", ha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rft_data, 16)); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2083, + "RFT_ID failed, rejected request rft_rsp:\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2080, + sns_cmd->p.rft_data, 16); rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2073, + "RFT_ID exiting normally.\n"); } return (rval); @@ -1054,47 +1073,48 @@ qla2x00_sns_rft_id(scsi_qla_host_t *ha) * Returns 0 on success. */ static int -qla2x00_sns_rnn_id(scsi_qla_host_t *ha) +qla2x00_sns_rnn_id(scsi_qla_host_t *vha) { int rval; - + struct qla_hw_data *ha = vha->hw; struct sns_cmd_pkt *sns_cmd; /* Issue RNN_ID. */ /* Prepare SNS command request. */ - sns_cmd = qla2x00_prep_sns_cmd(ha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN, + sns_cmd = qla2x00_prep_sns_cmd(vha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN, RNN_ID_SNS_DATA_SIZE); /* Prepare SNS command arguments -- port_id, nodename. */ - sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa; - sns_cmd->p.cmd.param[1] = ha->d_id.b.area; - sns_cmd->p.cmd.param[2] = ha->d_id.b.domain; + sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa; + sns_cmd->p.cmd.param[1] = vha->d_id.b.area; + sns_cmd->p.cmd.param[2] = vha->d_id.b.domain; - sns_cmd->p.cmd.param[4] = ha->node_name[7]; - sns_cmd->p.cmd.param[5] = ha->node_name[6]; - sns_cmd->p.cmd.param[6] = ha->node_name[5]; - sns_cmd->p.cmd.param[7] = ha->node_name[4]; - sns_cmd->p.cmd.param[8] = ha->node_name[3]; - sns_cmd->p.cmd.param[9] = ha->node_name[2]; - sns_cmd->p.cmd.param[10] = ha->node_name[1]; - sns_cmd->p.cmd.param[11] = ha->node_name[0]; + sns_cmd->p.cmd.param[4] = vha->node_name[7]; + sns_cmd->p.cmd.param[5] = vha->node_name[6]; + sns_cmd->p.cmd.param[6] = vha->node_name[5]; + sns_cmd->p.cmd.param[7] = vha->node_name[4]; + sns_cmd->p.cmd.param[8] = vha->node_name[3]; + sns_cmd->p.cmd.param[9] = vha->node_name[2]; + sns_cmd->p.cmd.param[10] = vha->node_name[1]; + sns_cmd->p.cmd.param[11] = vha->node_name[0]; /* Execute SNS command. */ - rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2, + rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RNN_ID Send SNS failed (%d).\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x204a, + "RNN_ID Send SNS failed (%d).\n", rval); } else if (sns_cmd->p.rnn_data[8] != 0x80 || sns_cmd->p.rnn_data[9] != 0x02) { - DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected request, " - "rnn_rsp:\n", ha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rnn_data, 16)); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207b, + "RNN_ID failed, rejected request, rnn_rsp:\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x207c, + sns_cmd->p.rnn_data, 16); rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x204c, + "RNN_ID exiting normally.\n"); } return (rval); @@ -1106,26 +1126,32 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *ha) * * Returns 0 on success. */ -int -qla2x00_mgmt_svr_login(scsi_qla_host_t *ha) +static int +qla2x00_mgmt_svr_login(scsi_qla_host_t *vha) { - int ret; + int ret, rval; uint16_t mb[MAILBOX_REGISTER_COUNT]; - + struct qla_hw_data *ha = vha->hw; ret = QLA_SUCCESS; - if (ha->flags.management_server_logged_in) + if (vha->flags.management_server_logged_in) return ret; - ha->isp_ops->fabric_login(ha, ha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa, - mb, BIT_1); - if (mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: " - "loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n", - __func__, ha->host_no, ha->mgmt_svr_loop_id, mb[0], mb[1], - mb[2], mb[6], mb[7])); + rval = ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, + 0xfa, mb, BIT_1|BIT_0); + if (rval != QLA_SUCCESS || mb[0] != MBS_COMMAND_COMPLETE) { + if (rval == QLA_MEMORY_ALLOC_FAILED) + ql_dbg(ql_dbg_disc, vha, 0x2085, + "Failed management_server login: loopid=%x " + "rval=%d\n", vha->mgmt_svr_loop_id, rval); + else + ql_dbg(ql_dbg_disc, vha, 0x2024, + "Failed management_server login: loopid=%x " + "mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x.\n", + vha->mgmt_svr_loop_id, mb[0], mb[1], mb[2], mb[6], + mb[7]); ret = QLA_FUNCTION_FAILED; } else - ha->flags.management_server_logged_in = 1; + vha->flags.management_server_logged_in = 1; return ret; } @@ -1139,17 +1165,17 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *ha) * Returns a pointer to the @ha's ms_iocb. */ void * -qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, +qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) { ms_iocb_entry_t *ms_pkt; - + struct qla_hw_data *ha = vha->hw; ms_pkt = ha->ms_iocb; memset(ms_pkt, 0, sizeof(ms_iocb_entry_t)); ms_pkt->entry_type = MS_IOCB_TYPE; ms_pkt->entry_count = 1; - SET_TARGET_ID(ha, ms_pkt->loop_id, ha->mgmt_svr_loop_id); + SET_TARGET_ID(ha, ms_pkt->loop_id, vha->mgmt_svr_loop_id); ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG); ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); @@ -1177,17 +1203,18 @@ qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, * Returns a pointer to the @ha's ms_iocb. */ void * -qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, +qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) { struct ct_entry_24xx *ct_pkt; + struct qla_hw_data *ha = vha->hw; ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_count = 1; - ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id); + ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id); ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); @@ -1201,14 +1228,15 @@ qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; - ct_pkt->vp_index = ha->vp_idx; + ct_pkt->vp_index = vha->vp_idx; return ct_pkt; } static inline ms_iocb_entry_t * -qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size) +qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size) { + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt = ha->ms_iocb; struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; @@ -1253,7 +1281,7 @@ qla2x00_prep_ct_fdmi_req(struct ct_sns_req *ct_req, uint16_t cmd, * Returns 0 on success. */ static int -qla2x00_fdmi_rhba(scsi_qla_host_t *ha) +qla2x00_fdmi_rhba(scsi_qla_host_t *vha) { int rval, alen; uint32_t size, sn; @@ -1263,11 +1291,12 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) struct ct_sns_rsp *ct_rsp; uint8_t *entries; struct ct_fdmi_hba_attr *eiter; + struct qla_hw_data *ha = vha->hw; /* Issue RHBA */ /* Prepare common MS IOCB */ /* Request size adjusted after CT preparation */ - ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RHBA_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RHBA_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RHBA_CMD, @@ -1275,9 +1304,9 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) ct_rsp = &ha->ct_sns->p.rsp; /* Prepare FDMI command arguments -- attribute block, attributes. */ - memcpy(ct_req->req.rhba.hba_identifier, ha->port_name, WWN_SIZE); + memcpy(ct_req->req.rhba.hba_identifier, vha->port_name, WWN_SIZE); ct_req->req.rhba.entry_count = __constant_cpu_to_be32(1); - memcpy(ct_req->req.rhba.port_name, ha->port_name, WWN_SIZE); + memcpy(ct_req->req.rhba.port_name, vha->port_name, WWN_SIZE); size = 2 * WWN_SIZE + 4 + 4; /* Attributes */ @@ -1289,26 +1318,27 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) eiter = (struct ct_fdmi_hba_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_HBA_NODE_NAME); eiter->len = __constant_cpu_to_be16(4 + WWN_SIZE); - memcpy(eiter->a.node_name, ha->node_name, WWN_SIZE); + memcpy(eiter->a.node_name, vha->node_name, WWN_SIZE); size += 4 + WWN_SIZE; - DEBUG13(printk("%s(%ld): NODENAME=%02x%02x%02x%02x%02x%02x%02x%02x.\n", - __func__, ha->host_no, - eiter->a.node_name[0], eiter->a.node_name[1], eiter->a.node_name[2], - eiter->a.node_name[3], eiter->a.node_name[4], eiter->a.node_name[5], - eiter->a.node_name[6], eiter->a.node_name[7])); + ql_dbg(ql_dbg_disc, vha, 0x2025, + "NodeName = %02x%02x%02x%02x%02x%02x%02x%02x.\n", + eiter->a.node_name[0], eiter->a.node_name[1], + eiter->a.node_name[2], eiter->a.node_name[3], + eiter->a.node_name[4], eiter->a.node_name[5], + eiter->a.node_name[6], eiter->a.node_name[7]); /* Manufacturer. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_HBA_MANUFACTURER); - strcpy(eiter->a.manufacturer, "QLogic Corporation"); + strcpy(eiter->a.manufacturer, "FUSIONIO"); alen = strlen(eiter->a.manufacturer); alen += (alen & 3) ? (4 - (alen & 3)) : 4; eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, ha->host_no, - eiter->a.manufacturer)); + ql_dbg(ql_dbg_disc, vha, 0x2026, + "Manufacturer = %s.\n", eiter->a.manufacturer); /* Serial number. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); @@ -1320,8 +1350,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, ha->host_no, - eiter->a.serial_num)); + ql_dbg(ql_dbg_disc, vha, 0x2027, + "Serial no. = %s.\n", eiter->a.serial_num); /* Model name. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); @@ -1332,21 +1362,20 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, ha->host_no, - eiter->a.model)); + ql_dbg(ql_dbg_disc, vha, 0x2028, + "Model Name = %s.\n", eiter->a.model); /* Model description. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL_DESCRIPTION); - if (ha->model_desc) - strncpy(eiter->a.model_desc, ha->model_desc, 80); + strncpy(eiter->a.model_desc, ha->model_desc, 80); alen = strlen(eiter->a.model_desc); alen += (alen & 3) ? (4 - (alen & 3)) : 4; eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, ha->host_no, - eiter->a.model_desc)); + ql_dbg(ql_dbg_disc, vha, 0x2029, + "Model Desc = %s.\n", eiter->a.model_desc); /* Hardware version. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); @@ -1357,8 +1386,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, ha->host_no, - eiter->a.hw_version)); + ql_dbg(ql_dbg_disc, vha, 0x202a, + "Hardware ver = %s.\n", eiter->a.hw_version); /* Driver version. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); @@ -1369,8 +1398,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, ha->host_no, - eiter->a.driver_version)); + ql_dbg(ql_dbg_disc, vha, 0x202b, + "Driver ver = %s.\n", eiter->a.driver_version); /* Option ROM version. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); @@ -1381,57 +1410,59 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, ha->host_no, - eiter->a.orom_version)); + ql_dbg(ql_dbg_disc, vha , 0x202c, + "Optrom vers = %s.\n", eiter->a.orom_version); /* Firmware version */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION); - ha->isp_ops->fw_version_str(ha, eiter->a.fw_version, + ha->isp_ops->fw_version_str(vha, eiter->a.fw_version, sizeof(eiter->a.fw_version)); alen = strlen(eiter->a.fw_version); alen += (alen & 3) ? (4 - (alen & 3)) : 4; eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, ha->host_no, - eiter->a.fw_version)); + ql_dbg(ql_dbg_disc, vha, 0x202d, + "Firmware vers = %s.\n", eiter->a.fw_version); /* Update MS request size. */ - qla2x00_update_ms_fdmi_iocb(ha, size + 16); + qla2x00_update_ms_fdmi_iocb(vha, size + 16); - DEBUG13(printk("%s(%ld): RHBA identifier=" - "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__, - ha->host_no, ct_req->req.rhba.hba_identifier[0], + ql_dbg(ql_dbg_disc, vha, 0x202e, + "RHBA identifier = " + "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", + ct_req->req.rhba.hba_identifier[0], ct_req->req.rhba.hba_identifier[1], ct_req->req.rhba.hba_identifier[2], ct_req->req.rhba.hba_identifier[3], ct_req->req.rhba.hba_identifier[4], ct_req->req.rhba.hba_identifier[5], ct_req->req.rhba.hba_identifier[6], - ct_req->req.rhba.hba_identifier[7], size)); - DEBUG13(qla2x00_dump_buffer(entries, size)); + ct_req->req.rhba.hba_identifier[7], size); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2076, + entries, size); /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RHBA issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RHBA") != + ql_dbg(ql_dbg_disc, vha, 0x2030, + "RHBA issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RHBA") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; if (ct_rsp->header.reason_code == CT_REASON_CANNOT_PERFORM && ct_rsp->header.explanation_code == CT_EXPL_ALREADY_REGISTERED) { - DEBUG2_13(printk("%s(%ld): HBA already registered.\n", - __func__, ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2034, + "HBA already registered.\n"); rval = QLA_ALREADY_REGISTERED; } } else { - DEBUG2(printk("scsi(%ld): RHBA exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2035, + "RHBA exiting normally.\n"); } return rval; @@ -1444,17 +1475,17 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) * Returns 0 on success. */ static int -qla2x00_fdmi_dhba(scsi_qla_host_t *ha) +qla2x00_fdmi_dhba(scsi_qla_host_t *vha) { int rval; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; /* Issue RPA */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, DHBA_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, DHBA_REQ_SIZE, DHBA_RSP_SIZE); /* Prepare CT request */ @@ -1463,28 +1494,28 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *ha) ct_rsp = &ha->ct_sns->p.rsp; /* Prepare FDMI command arguments -- portname. */ - memcpy(ct_req->req.dhba.port_name, ha->port_name, WWN_SIZE); + memcpy(ct_req->req.dhba.port_name, vha->port_name, WWN_SIZE); - DEBUG13(printk("%s(%ld): DHBA portname=" - "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, ha->host_no, + ql_dbg(ql_dbg_disc, vha, 0x2036, + "DHBA portname = %02x%02x%02x%02x%02x%02x%02x%02x.\n", ct_req->req.dhba.port_name[0], ct_req->req.dhba.port_name[1], ct_req->req.dhba.port_name[2], ct_req->req.dhba.port_name[3], ct_req->req.dhba.port_name[4], ct_req->req.dhba.port_name[5], - ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7])); + ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7]); /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): DHBA issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "DHBA") != + ql_dbg(ql_dbg_disc, vha, 0x2037, + "DHBA issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "DHBA") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): DHBA exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2038, + "DHBA exiting normally.\n"); } return rval; @@ -1497,11 +1528,11 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *ha) * Returns 0 on success. */ static int -qla2x00_fdmi_rpa(scsi_qla_host_t *ha) +qla2x00_fdmi_rpa(scsi_qla_host_t *vha) { int rval, alen; uint32_t size, max_frame_size; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; @@ -1512,7 +1543,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) /* Issue RPA */ /* Prepare common MS IOCB */ /* Request size adjusted after CT preparation */ - ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RPA_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RPA_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RPA_CMD, @@ -1520,7 +1551,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) ct_rsp = &ha->ct_sns->p.rsp; /* Prepare FDMI command arguments -- attribute block, attributes. */ - memcpy(ct_req->req.rpa.port_name, ha->port_name, WWN_SIZE); + memcpy(ct_req->req.rpa.port_name, vha->port_name, WWN_SIZE); size = WWN_SIZE + 4; /* Attributes */ @@ -1535,14 +1566,19 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) eiter->a.fc4_types[2] = 0x01; size += 4 + 32; - DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__, ha->host_no, - eiter->a.fc4_types[2], eiter->a.fc4_types[1])); + ql_dbg(ql_dbg_disc, vha, 0x2039, + "FC4_TYPES=%02x %02x.\n", + eiter->a.fc4_types[2], + eiter->a.fc4_types[1]); /* Supported speed. */ eiter = (struct ct_fdmi_port_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_PORT_SUPPORT_SPEED); eiter->len = __constant_cpu_to_be16(4 + 4); - if (IS_QLA25XX(ha)) + if (IS_CNA_CAPABLE(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32( + FDMI_PORT_SPEED_10GB); + else if (IS_QLA25XX(ha)) eiter->a.sup_speed = __constant_cpu_to_be32( FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB| FDMI_PORT_SPEED_4GB|FDMI_PORT_SPEED_8GB); @@ -1558,8 +1594,8 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) FDMI_PORT_SPEED_1GB); size += 4 + 4; - DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, ha->host_no, - eiter->a.sup_speed)); + ql_dbg(ql_dbg_disc, vha, 0x203a, + "Supported_Speed=%x.\n", eiter->a.sup_speed); /* Current speed. */ eiter = (struct ct_fdmi_port_attr *) (entries + size); @@ -1582,6 +1618,14 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) eiter->a.cur_speed = __constant_cpu_to_be32(FDMI_PORT_SPEED_8GB); break; + case PORT_SPEED_10GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_10GB); + break; + case PORT_SPEED_16GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_16GB); + break; default: eiter->a.cur_speed = __constant_cpu_to_be32(FDMI_PORT_SPEED_UNKNOWN); @@ -1589,8 +1633,8 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) } size += 4 + 4; - DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, ha->host_no, - eiter->a.cur_speed)); + ql_dbg(ql_dbg_disc, vha, 0x203b, + "Current_Speed=%x.\n", eiter->a.cur_speed); /* Max frame size. */ eiter = (struct ct_fdmi_port_attr *) (entries + size); @@ -1602,8 +1646,8 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) eiter->a.max_frame_size = cpu_to_be32(max_frame_size); size += 4 + 4; - DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, ha->host_no, - eiter->a.max_frame_size)); + ql_dbg(ql_dbg_disc, vha, 0x203c, + "Max_Frame_Size=%x.\n", eiter->a.max_frame_size); /* OS device name. */ eiter = (struct ct_fdmi_port_attr *) (entries + size); @@ -1614,51 +1658,52 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, ha->host_no, - eiter->a.os_dev_name)); + ql_dbg(ql_dbg_disc, vha, 0x204b, + "OS_Device_Name=%s.\n", eiter->a.os_dev_name); /* Hostname. */ - if (strlen(fc_host_system_hostname(ha->host))) { + if (strlen(fc_host_system_hostname(vha->host))) { ct_req->req.rpa.attrs.count = __constant_cpu_to_be32(FDMI_PORT_ATTR_COUNT); eiter = (struct ct_fdmi_port_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_PORT_HOST_NAME); snprintf(eiter->a.host_name, sizeof(eiter->a.host_name), - "%s", fc_host_system_hostname(ha->host)); + "%s", fc_host_system_hostname(vha->host)); alen = strlen(eiter->a.host_name); alen += (alen & 3) ? (4 - (alen & 3)) : 4; eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): HOSTNAME=%s.\n", __func__, - ha->host_no, eiter->a.host_name)); + ql_dbg(ql_dbg_disc, vha, 0x203d, + "HostName=%s.\n", eiter->a.host_name); } /* Update MS request size. */ - qla2x00_update_ms_fdmi_iocb(ha, size + 16); + qla2x00_update_ms_fdmi_iocb(vha, size + 16); - DEBUG13(printk("%s(%ld): RPA portname=" - "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__, - ha->host_no, ct_req->req.rpa.port_name[0], - ct_req->req.rpa.port_name[1], ct_req->req.rpa.port_name[2], - ct_req->req.rpa.port_name[3], ct_req->req.rpa.port_name[4], - ct_req->req.rpa.port_name[5], ct_req->req.rpa.port_name[6], - ct_req->req.rpa.port_name[7], size)); - DEBUG13(qla2x00_dump_buffer(entries, size)); + ql_dbg(ql_dbg_disc, vha, 0x203e, + "RPA portname= %02x%02x%02x%02x%02X%02x%02x%02x size=%d.\n", + ct_req->req.rpa.port_name[0], ct_req->req.rpa.port_name[1], + ct_req->req.rpa.port_name[2], ct_req->req.rpa.port_name[3], + ct_req->req.rpa.port_name[4], ct_req->req.rpa.port_name[5], + ct_req->req.rpa.port_name[6], ct_req->req.rpa.port_name[7], + size); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2079, + entries, size); /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RPA issue IOCB failed (%d).\n", - ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RPA") != + ql_dbg(ql_dbg_disc, vha, 0x2040, + "RPA issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RPA") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("scsi(%ld): RPA exiting normally.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2041, + "RPA exiting nornally.\n"); } return rval; @@ -1671,34 +1716,32 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla2x00_fdmi_register(scsi_qla_host_t *ha) +qla2x00_fdmi_register(scsi_qla_host_t *vha) { int rval; + struct qla_hw_data *ha = vha->hw; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - DEBUG2(printk("scsi(%ld): FDMI unsupported on " - "ISP2100/ISP2200.\n", ha->host_no)); - return QLA_SUCCESS; - } + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return QLA_FUNCTION_FAILED; - rval = qla2x00_mgmt_svr_login(ha); + rval = qla2x00_mgmt_svr_login(vha); if (rval) return rval; - rval = qla2x00_fdmi_rhba(ha); + rval = qla2x00_fdmi_rhba(vha); if (rval) { if (rval != QLA_ALREADY_REGISTERED) return rval; - rval = qla2x00_fdmi_dhba(ha); + rval = qla2x00_fdmi_dhba(vha); if (rval) return rval; - rval = qla2x00_fdmi_rhba(ha); + rval = qla2x00_fdmi_rhba(vha); if (rval) return rval; } - rval = qla2x00_fdmi_rpa(ha); + rval = qla2x00_fdmi_rpa(vha); return rval; } @@ -1711,11 +1754,11 @@ qla2x00_fdmi_register(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; uint16_t i; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; @@ -1723,14 +1766,14 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list) if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GFPN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GFPN_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFPN_ID_REQ_SIZE, GFPN_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFPN_ID_CMD, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFPN_ID_CMD, GFPN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -1740,15 +1783,17 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list) ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa; /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GFPN_ID issue IOCB " - "failed (%d).\n", ha->host_no, rval)); - } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, + ql_dbg(ql_dbg_disc, vha, 0x2023, + "GFPN_ID issue IOCB failed (%d).\n", rval); + break; + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GFPN_ID") != QLA_SUCCESS) { rval = QLA_FUNCTION_FAILED; + break; } else { /* Save fabric portname */ memcpy(list[i].fabric_port_name, @@ -1764,17 +1809,17 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list) } static inline void * -qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size, +qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) { struct ct_entry_24xx *ct_pkt; - + struct qla_hw_data *ha = vha->hw; ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_count = 1; - ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id); + ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id); ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); @@ -1788,7 +1833,7 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size, ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; - ct_pkt->vp_index = ha->vp_idx; + ct_pkt->vp_index = vha->vp_idx; return ct_pkt; } @@ -1817,11 +1862,11 @@ qla24xx_prep_ct_fm_req(struct ct_sns_req *ct_req, uint16_t cmd, * Returns 0 on success. */ int -qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) +qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) { int rval; uint16_t i; - + struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; @@ -1831,14 +1876,14 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) if (!ha->flags.gpsc_supported) return QLA_FUNCTION_FAILED; - rval = qla2x00_mgmt_svr_login(ha); + rval = qla2x00_mgmt_svr_login(vha); if (rval) return rval; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GFPN_ID */ /* Prepare common MS IOCB */ - ms_pkt = qla24xx_prep_ms_fm_iocb(ha, GPSC_REQ_SIZE, + ms_pkt = qla24xx_prep_ms_fm_iocb(vha, GPSC_REQ_SIZE, GPSC_RSP_SIZE); /* Prepare CT request */ @@ -1851,13 +1896,13 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) WWN_SIZE); /* Execute MS IOCB */ - rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GPSC issue IOCB " - "failed (%d).\n", ha->host_no, rval)); - } else if ((rval = qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, + ql_dbg(ql_dbg_disc, vha, 0x2059, + "GPSC issue IOCB failed (%d).\n", rval); + } else if ((rval = qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GPSC")) != QLA_SUCCESS) { /* FM command unsupported? */ if (rval == QLA_INVALID_COMMAND && @@ -1865,9 +1910,9 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) CT_REASON_INVALID_COMMAND_CODE || ct_rsp->header.reason_code == CT_REASON_COMMAND_UNSUPPORTED)) { - DEBUG2(printk("scsi(%ld): GPSC command " - "unsupported, disabling query...\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x205a, + "GPSC command unsupported, disabling " + "query.\n"); ha->flags.gpsc_supported = 0; rval = QLA_FUNCTION_FAILED; break; @@ -1885,14 +1930,18 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) case BIT_13: list[i].fp_speed = PORT_SPEED_4GB; break; + case BIT_12: + list[i].fp_speed = PORT_SPEED_10GB; + break; case BIT_11: list[i].fp_speed = PORT_SPEED_8GB; break; } - DEBUG2_3(printk("scsi(%ld): GPSC ext entry - " - "fpn %02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x " - "speed=%04x.\n", ha->host_no, + ql_dbg(ql_dbg_disc, vha, 0x205b, + "GPSC ext entry - fpn " + "%02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x " + "speed=%04x.\n", list[i].fabric_port_name[0], list[i].fabric_port_name[1], list[i].fabric_port_name[2], @@ -1902,7 +1951,7 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) list[i].fabric_port_name[6], list[i].fabric_port_name[7], be16_to_cpu(ct_rsp->rsp.gpsc.speeds), - be16_to_cpu(ct_rsp->rsp.gpsc.speed))); + be16_to_cpu(ct_rsp->rsp.gpsc.speed)); } /* Last device exit. */ @@ -1912,3 +1961,73 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) return (rval); } + +/** + * qla2x00_gff_id() - SNS Get FC-4 Features (GFF_ID) query. + * + * @ha: HA context + * @list: switch info entries to populate + * + */ +void +qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) +{ + int rval; + uint16_t i; + + ms_iocb_entry_t *ms_pkt; + struct ct_sns_req *ct_req; + struct ct_sns_rsp *ct_rsp; + struct qla_hw_data *ha = vha->hw; + uint8_t fcp_scsi_features = 0; + + for (i = 0; i < ha->max_fibre_devices; i++) { + /* Set default FC4 Type as UNKNOWN so the default is to + * Process this port */ + list[i].fc4_type = FC4_TYPE_UNKNOWN; + + /* Do not attempt GFF_ID if we are not FWI_2 capable */ + if (!IS_FWI2_CAPABLE(ha)) + continue; + + /* Prepare common MS IOCB */ + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFF_ID_REQ_SIZE, + GFF_ID_RSP_SIZE); + + /* Prepare CT request */ + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFF_ID_CMD, + GFF_ID_RSP_SIZE); + ct_rsp = &ha->ct_sns->p.rsp; + + /* Prepare CT arguments -- port_id */ + ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain; + ct_req->req.port_id.port_id[1] = list[i].d_id.b.area; + ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa; + + /* Execute MS IOCB */ + rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma, + sizeof(ms_iocb_entry_t)); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_disc, vha, 0x205c, + "GFF_ID issue IOCB failed (%d).\n", rval); + } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, + "GFF_ID") != QLA_SUCCESS) { + ql_dbg(ql_dbg_disc, vha, 0x205d, + "GFF_ID IOCB status had a failure status code.\n"); + } else { + fcp_scsi_features = + ct_rsp->rsp.gff_id.fc4_features[GFF_FCP_SCSI_OFFSET]; + fcp_scsi_features &= 0x0f; + + if (fcp_scsi_features) + list[i].fc4_type = FC4_TYPE_FCP_SCSI; + else + list[i].fc4_type = FC4_TYPE_OTHER; + } + + /* Last device exit. */ + if (list[i].d_id.b.rsvd_1 != 0) + break; + } +} diff --git a/qla2x00t/qla_init.c b/qla2x00t/qla_init.c index 865c21623..4a65cc235 100644 --- a/qla2x00t/qla_init.c +++ b/qla2x00t/qla_init.c @@ -1,10 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla_gbl.h" #include #include @@ -15,33 +16,413 @@ #include #endif -#include "qla2x_tgt.h" - #ifndef CONFIG_SCSI_QLA2XXX_TARGET #warning "CONFIG_SCSI_QLA2XXX_TARGET is not set" +#else +#include "qla2x_tgt.h" #endif /* * QLogic ISP2x00 Hardware Support Function Prototypes. */ static int qla2x00_isp_firmware(scsi_qla_host_t *); -static void qla2x00_resize_request_q(scsi_qla_host_t *); static int qla2x00_setup_chip(scsi_qla_host_t *); -static void qla2x00_init_response_q_entries(scsi_qla_host_t *); static int qla2x00_init_rings(scsi_qla_host_t *); static int qla2x00_fw_ready(scsi_qla_host_t *); static int qla2x00_configure_hba(scsi_qla_host_t *); static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *, struct list_head *); -static int qla2x00_device_resync(scsi_qla_host_t *); static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *, uint16_t *); static int qla2x00_restart_isp(scsi_qla_host_t *); -static int qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev); - static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *); static int qla84xx_init_chip(scsi_qla_host_t *); +static int qla25xx_init_queues(struct qla_hw_data *); +int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *); + +/* SRB Extensions ---------------------------------------------------------- */ + +void +qla2x00_sp_timeout(unsigned long __data) +{ + srb_t *sp = (srb_t *)__data; + struct srb_iocb *iocb; + fc_port_t *fcport = sp->fcport; + struct qla_hw_data *ha = fcport->vha->hw; + struct req_que *req; + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + req = ha->req_q_map[0]; + req->outstanding_cmds[sp->handle] = NULL; + iocb = &sp->u.iocb_cmd; + iocb->timeout(sp); + sp->free(fcport->vha, sp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void +qla2x00_sp_free(void *data, void *ptr) +{ + srb_t *sp = (srb_t *)ptr; + struct srb_iocb *iocb = &sp->u.iocb_cmd; + struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + + del_timer(&iocb->timer); + mempool_free(sp, vha->hw->srb_mempool); + + QLA_VHA_MARK_NOT_BUSY(vha); +} + +/* Asynchronous Login/Logout Routines -------------------------------------- */ + +static inline unsigned long +qla2x00_get_async_timeout(struct scsi_qla_host *vha) +{ + unsigned long tmo; + struct qla_hw_data *ha = vha->hw; + + /* Firmware should use switch negotiated r_a_tov for timeout. */ + tmo = ha->r_a_tov / 10 * 2; + if (!IS_FWI2_CAPABLE(ha)) { + /* + * Except for earlier ISPs where the timeout is seeded from the + * initialization control block. + */ + tmo = ha->login_timeout; + } + return tmo; +} + +static void +qla2x00_async_iocb_timeout(void *data) +{ + srb_t *sp = (srb_t *)data; + fc_port_t *fcport = sp->fcport; + + ql_dbg(ql_dbg_disc, fcport->vha, 0x2071, + "Async-%s timeout - hdl=%x portid=%02x%02x%02x.\n", + sp->name, sp->handle, fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa); + + fcport->flags &= ~FCF_ASYNC_SENT; + if (sp->type == SRB_LOGIN_CMD) { + struct srb_iocb *lio = &sp->u.iocb_cmd; + qla2x00_post_async_logout_work(fcport->vha, fcport, NULL); + /* Retry as needed. */ + lio->u.logio.data[0] = MBS_COMMAND_ERROR; + lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? + QLA_LOGIO_LOGIN_RETRIED : 0; + qla2x00_post_async_login_done_work(fcport->vha, fcport, + lio->u.logio.data); + } +} + +static void +qla2x00_async_login_sp_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t*)ptr; + struct srb_iocb *lio = &sp->u.iocb_cmd; + struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + + if (!test_bit(UNLOADING, &vha->dpc_flags)) + qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport, + lio->u.logio.data); + sp->free(sp->fcport->vha, sp); +} + +int +qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, + uint16_t *data) +{ + srb_t *sp; + struct srb_iocb *lio; + int rval; + + rval = QLA_FUNCTION_FAILED; + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + sp->type = SRB_LOGIN_CMD; + sp->name = "login"; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + lio = &sp->u.iocb_cmd; + lio->timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_login_sp_done; + lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI; + if (data[1] & QLA_LOGIO_LOGIN_RETRIED) + lio->u.logio.flags |= SRB_LOGIN_RETRIED; + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0x2072, + "Async-login - hdl=%x, loopid=%x portid=%02x%02x%02x " + "retries=%d.\n", sp->handle, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, + fcport->login_retry); + return rval; + +done_free_sp: + sp->free(fcport->vha, sp); +done: + return rval; +} + +static void +qla2x00_async_logout_sp_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t *)ptr; + struct srb_iocb *lio = &sp->u.iocb_cmd; + struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + + if (!test_bit(UNLOADING, &vha->dpc_flags)) + qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport, + lio->u.logio.data); + sp->free(sp->fcport->vha, sp); +} + +int +qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + srb_t *sp; + struct srb_iocb *lio; + int rval; + + rval = QLA_FUNCTION_FAILED; + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + sp->type = SRB_LOGOUT_CMD; + sp->name = "logout"; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + lio = &sp->u.iocb_cmd; + lio->timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_logout_sp_done; + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0x2070, + "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x.\n", + sp->handle, fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); + return rval; + +done_free_sp: + sp->free(fcport->vha, sp); +done: + return rval; +} + +static void +qla2x00_async_adisc_sp_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t *)ptr; + struct srb_iocb *lio = &sp->u.iocb_cmd; + struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + + if (!test_bit(UNLOADING, &vha->dpc_flags)) + qla2x00_post_async_adisc_done_work(sp->fcport->vha, sp->fcport, + lio->u.logio.data); + sp->free(sp->fcport->vha, sp); +} + +int +qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, + uint16_t *data) +{ + srb_t *sp; + struct srb_iocb *lio; + int rval; + + rval = QLA_FUNCTION_FAILED; + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + sp->type = SRB_ADISC_CMD; + sp->name = "adisc"; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + lio = &sp->u.iocb_cmd; + lio->timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_adisc_sp_done; + if (data[1] & QLA_LOGIO_LOGIN_RETRIED) + lio->u.logio.flags |= SRB_LOGIN_RETRIED; + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0x206f, + "Async-adisc - hdl=%x loopid=%x portid=%02x%02x%02x.\n", + sp->handle, fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); + return rval; + +done_free_sp: + sp->free(fcport->vha, sp); +done: + return rval; +} + +static void +qla2x00_async_tm_cmd_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t*)ptr; + struct srb_iocb *iocb = &sp->u.iocb_cmd; + struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + uint32_t flags; + uint16_t lun; + int rval; + + if (!test_bit(UNLOADING, &vha->dpc_flags)) { + flags = iocb->u.tmf.flags; + lun = (uint16_t)iocb->u.tmf.lun; + + /* Issue Marker IOCB */ + rval = qla2x00_marker(vha, vha->hw->req_q_map[0], + vha->hw->rsp_q_map[0], sp->fcport->loop_id, lun, + flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID); + + if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) { + ql_dbg(ql_dbg_taskm, vha, 0x8030, + "TM IOCB failed (%x).\n", rval); + } + } + sp->free(sp->fcport->vha, sp); +} + +int +qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t tm_flags, uint32_t lun, + uint32_t tag) +{ + struct scsi_qla_host *vha = fcport->vha; + srb_t *sp; + struct srb_iocb *tcf; + int rval; + + rval = QLA_FUNCTION_FAILED; + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + sp->type = SRB_TM_CMD; + sp->name = "tmf"; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + tcf = &sp->u.iocb_cmd; + tcf->u.tmf.flags = tm_flags; + tcf->u.tmf.lun = lun; + tcf->u.tmf.data = tag; + tcf->timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_tm_cmd_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_taskm, vha, 0x802f, + "Async-tmf hdl=%x loop-id=%x portid=%02x%02x%02x.\n", + sp->handle, fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); + return rval; + +done_free_sp: + sp->free(fcport->vha, sp); +done: + return rval; +} + +void +qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport, + uint16_t *data) +{ + int rval; + + switch (data[0]) { + case MBS_COMMAND_COMPLETE: + /* + * Driver must validate login state - If PRLI not complete, + * force a relogin attempt via implicit LOGO, PLOGI, and PRLI + * requests. + */ + rval = qla2x00_get_port_database(vha, fcport, 0); + if (rval == QLA_NOT_LOGGED_IN) { + fcport->flags &= ~FCF_ASYNC_SENT; + fcport->flags |= FCF_LOGIN_NEEDED; + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + break; + } + + if (rval != QLA_SUCCESS) { + qla2x00_post_async_logout_work(vha, fcport, NULL); + qla2x00_post_async_login_work(vha, fcport, NULL); + break; + } + if (fcport->flags & FCF_FCP2_DEVICE) { + qla2x00_post_async_adisc_work(vha, fcport, data); + break; + } + qla2x00_update_fcport(vha, fcport); + break; + case MBS_COMMAND_ERROR: + fcport->flags &= ~FCF_ASYNC_SENT; + if (data[1] & QLA_LOGIO_LOGIN_RETRIED) + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + else + qla2x00_mark_device_lost(vha, fcport, 1, 0); + break; + case MBS_PORT_ID_USED: + fcport->loop_id = data[1]; + qla2x00_post_async_logout_work(vha, fcport, NULL); + qla2x00_post_async_login_work(vha, fcport, NULL); + break; + case MBS_LOOP_ID_USED: + fcport->loop_id++; + rval = qla2x00_find_new_loop_id(vha, fcport); + if (rval != QLA_SUCCESS) { + fcport->flags &= ~FCF_ASYNC_SENT; + qla2x00_mark_device_lost(vha, fcport, 1, 0); + break; + } + qla2x00_post_async_login_work(vha, fcport, NULL); + break; + } + return; +} + +void +qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, + uint16_t *data) +{ + qla2x00_mark_device_lost(vha, fcport, 1, 0); + return; +} + +void +qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport, + uint16_t *data) +{ + if (data[0] == MBS_COMMAND_COMPLETE) { + qla2x00_update_fcport(vha, fcport); + + return; + } + + /* Retry login. */ + fcport->flags &= ~FCF_ASYNC_SENT; + if (data[1] & QLA_LOGIO_LOGIN_RETRIED) + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + else + qla2x00_mark_device_lost(vha, fcport, 1, 0); + + return; +} /****************************************************************************/ /* QLogic ISP2x00 Hardware Support Functions. */ @@ -58,79 +439,111 @@ static int qla84xx_init_chip(scsi_qla_host_t *); * 0 = success */ int -qla2x00_initialize_adapter(scsi_qla_host_t *ha) +qla2x00_initialize_adapter(scsi_qla_host_t *vha) { int rval; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; /* Clear adapter flags. */ - ha->flags.online = 0; - ha->flags.reset_active = 0; - atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); - atomic_set(&ha->loop_state, LOOP_DOWN); - ha->device_flags = DFLG_NO_CABLE; - ha->dpc_flags = 0; - ha->flags.management_server_logged_in = 0; - ha->marker_needed = 0; - ha->mbx_flags = 0; + vha->flags.online = 0; + ha->flags.chip_reset_done = 0; + vha->flags.reset_active = 0; + ha->atio_ignored = 0; + ha->flags.pci_channel_io_perm_failure = 0; + ha->flags.eeh_busy = 0; + ha->flags.thermal_supported = 1; + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + atomic_set(&vha->loop_state, LOOP_DOWN); + vha->device_flags = DFLG_NO_CABLE; + vha->dpc_flags = 0; + vha->flags.management_server_logged_in = 0; + vha->marker_needed = 0; ha->isp_abort_cnt = 0; ha->beacon_blink_led = 0; - set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); - qla_printk(KERN_INFO, ha, "Configuring PCI space...\n"); - rval = ha->isp_ops->pci_config(ha); + set_bit(0, ha->req_qid_map); + set_bit(0, ha->rsp_qid_map); + + ql_dbg(ql_dbg_init, vha, 0x0040, + "Configuring PCI space...\n"); + rval = ha->isp_ops->pci_config(vha); if (rval) { - DEBUG2(printk("scsi(%ld): Unable to configure PCI space.\n", - ha->host_no)); + ql_log(ql_log_warn, vha, 0x0044, + "Unable to configure PCI space.\n"); return (rval); } - ha->isp_ops->reset_chip(ha); + ha->isp_ops->reset_chip(vha); - ha->isp_ops->get_flash_version(ha, ha->request_ring); + rval = qla2xxx_get_flash_info(vha); + if (rval) { + ql_log(ql_log_fatal, vha, 0x004f, + "Unable to validate FLASH data.\n"); + return (rval); + } - qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n"); + ha->isp_ops->get_flash_version(vha, req->ring); + ql_dbg(ql_dbg_init, vha, 0x0061, + "Configure NVRAM parameters...\n"); - ha->isp_ops->nvram_config(ha); + ha->isp_ops->nvram_config(vha); if (ha->flags.disable_serdes) { /* Mask HBA via NVRAM settings? */ - qla_printk(KERN_INFO, ha, "Masking HBA WWPN " + ql_log(ql_log_info, vha, 0x0077, + "Masking HBA WWPN " "%02x%02x%02x%02x%02x%02x%02x%02x (via NVRAM).\n", - ha->port_name[0], ha->port_name[1], - ha->port_name[2], ha->port_name[3], - ha->port_name[4], ha->port_name[5], - ha->port_name[6], ha->port_name[7]); + vha->port_name[0], vha->port_name[1], + vha->port_name[2], vha->port_name[3], + vha->port_name[4], vha->port_name[5], + vha->port_name[6], vha->port_name[7]); return QLA_FUNCTION_FAILED; } - qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n"); + ql_dbg(ql_dbg_init, vha, 0x0078, + "Verifying loaded RISC code...\n"); - if (qla2x00_isp_firmware(ha) != QLA_SUCCESS) { - rval = ha->isp_ops->chip_diag(ha); + if (qla2x00_isp_firmware(vha) != QLA_SUCCESS) { + rval = ha->isp_ops->chip_diag(vha); if (rval) return (rval); - rval = qla2x00_setup_chip(ha); + rval = qla2x00_setup_chip(vha); if (rval) return (rval); - qla2xxx_get_flash_info(ha); } + if (IS_QLA84XX(ha)) { - ha->cs84xx = qla84xx_get_chip(ha); + ha->cs84xx = qla84xx_get_chip(vha); if (!ha->cs84xx) { - qla_printk(KERN_ERR, ha, + ql_log(ql_log_warn, vha, 0x00d0, "Unable to configure ISP84XX.\n"); return QLA_FUNCTION_FAILED; } } - rval = qla2x00_init_rings(ha); + rval = qla2x00_init_rings(vha); + ha->flags.chip_reset_done = 1; + + if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) { + /* Issue verify 84xx FW IOCB to complete 84xx initialization */ + rval = qla84xx_init_chip(vha); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x00d4, + "Unable to initialize ISP84XX.\n"); + qla84xx_put_chip(vha); + } + } + + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)) + qla24xx_read_fcp_prio_cfg(vha); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (rval == QLA_SUCCESS) { /* Enable target response to SCSI bus. */ - if (qla_tgt_mode_enabled(ha)) - qla2x00_send_enable_lun(ha, true); - else if (qla_ini_mode_enabled(ha)) - qla2x00_send_enable_lun(ha, false); + if (qla_tgt_mode_enabled(vha)) + qla2x00_send_enable_lun(vha, true); + else if (qla_ini_mode_enabled(vha)) + qla2x00_send_enable_lun(vha, false); } #endif @@ -144,11 +557,11 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla2100_pci_config(scsi_qla_host_t *ha) +qla2100_pci_config(scsi_qla_host_t *vha) { uint16_t w; - uint32_t d; unsigned long flags; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; pci_set_master(ha->pdev); @@ -158,10 +571,7 @@ qla2100_pci_config(scsi_qla_host_t *ha) w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); pci_write_config_word(ha->pdev, PCI_COMMAND, w); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -178,12 +588,12 @@ qla2100_pci_config(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla2300_pci_config(scsi_qla_host_t *ha) +qla2300_pci_config(scsi_qla_host_t *vha) { uint16_t w; - uint32_t d; unsigned long flags = 0; uint32_t cnt; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; pci_set_master(ha->pdev); @@ -243,10 +653,7 @@ qla2300_pci_config(scsi_qla_host_t *ha) pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -263,11 +670,11 @@ qla2300_pci_config(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla24xx_pci_config(scsi_qla_host_t *ha) +qla24xx_pci_config(scsi_qla_host_t *vha) { uint16_t w; - uint32_t d; unsigned long flags = 0; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; pci_set_master(ha->pdev); @@ -288,10 +695,7 @@ qla24xx_pci_config(scsi_qla_host_t *ha) if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) pcie_set_readrq(ha->pdev, 2048); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); ha->chip_revision = ha->pdev->revision; @@ -310,10 +714,10 @@ qla24xx_pci_config(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla25xx_pci_config(scsi_qla_host_t *ha) +qla25xx_pci_config(scsi_qla_host_t *vha) { uint16_t w; - uint32_t d; + struct qla_hw_data *ha = vha->hw; pci_set_master(ha->pdev); pci_try_set_mwi(ha->pdev); @@ -327,10 +731,7 @@ qla25xx_pci_config(scsi_qla_host_t *ha) if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) pcie_set_readrq(ha->pdev, 2048); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); ha->chip_revision = ha->pdev->revision; @@ -344,26 +745,31 @@ qla25xx_pci_config(scsi_qla_host_t *ha) * Returns 0 on success. */ static int -qla2x00_isp_firmware(scsi_qla_host_t *ha) +qla2x00_isp_firmware(scsi_qla_host_t *vha) { int rval; + uint16_t loop_id, topo, sw_cap; + uint8_t domain, area, al_pa; + struct qla_hw_data *ha = vha->hw; /* Assume loading risc code */ rval = QLA_FUNCTION_FAILED; if (ha->flags.disable_risc_code_load) { - DEBUG2(printk("scsi(%ld): RISC CODE NOT loaded\n", - ha->host_no)); - qla_printk(KERN_INFO, ha, "RISC CODE NOT loaded\n"); + ql_log(ql_log_info, vha, 0x0079, "RISC CODE NOT loaded.\n"); /* Verify checksum of loaded RISC code. */ - rval = qla2x00_verify_checksum(ha, ha->fw_srisc_address); + rval = qla2x00_verify_checksum(vha, ha->fw_srisc_address); + if (rval == QLA_SUCCESS) { + /* And, verify we are not in ROM code. */ + rval = qla2x00_get_adapter_id(vha, &loop_id, &al_pa, + &area, &domain, &topo, &sw_cap); + } } - if (rval) { - DEBUG2_3(printk("scsi(%ld): **** Load RISC code ****\n", - ha->host_no)); - } + if (rval) + ql_dbg(ql_dbg_init, vha, 0x007a, + "**** Load RISC code ****.\n"); return (rval); } @@ -375,13 +781,17 @@ qla2x00_isp_firmware(scsi_qla_host_t *ha) * Returns 0 on success. */ void -qla2x00_reset_chip(scsi_qla_host_t *ha) +qla2x00_reset_chip(scsi_qla_host_t *vha) { unsigned long flags = 0; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint32_t cnt; uint16_t cmd; + if (unlikely(pci_channel_offline(ha->pdev))) + return; + ha->isp_ops->disable_intrs(ha); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -508,6 +918,22 @@ qla2x00_reset_chip(scsi_qla_host_t *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } +/** + * qla81xx_reset_mpi() - Reset's MPI FW via Write MPI Register MBC. + * + * Returns 0 on success. + */ +int +qla81xx_reset_mpi(scsi_qla_host_t *vha) +{ + uint16_t mb[4] = {0x1010, 0, 1, 0}; + + if (!IS_QLA81XX(vha->hw)) + return QLA_SUCCESS; + + return qla81xx_write_mpi_register(vha, mb); +} + /** * qla24xx_reset_risc() - Perform full reset of ISP24xx RISC. * @ha: HA context @@ -515,13 +941,14 @@ qla2x00_reset_chip(scsi_qla_host_t *ha) * Returns 0 on success. */ static inline void -qla24xx_reset_risc(scsi_qla_host_t *ha) +qla24xx_reset_risc(scsi_qla_host_t *vha) { - int hw_evt = 0; unsigned long flags = 0; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t cnt, d2; uint16_t wd; + static int abts_cnt = 0; /* ISP abort retry counts */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -546,8 +973,6 @@ qla24xx_reset_risc(scsi_qla_host_t *ha) d2 = (uint32_t) RD_REG_WORD(®->mailbox0); barrier(); } - if (cnt == 0) - hw_evt = 1; /* Wait for soft-reset to complete. */ d2 = RD_REG_DWORD(®->ctrl_status); @@ -556,10 +981,24 @@ qla24xx_reset_risc(scsi_qla_host_t *ha) d2 = RD_REG_DWORD(®->ctrl_status); barrier(); } - if (cnt == 0 || hw_evt) - qla2xxx_hw_event_log(ha, HW_EVENT_RESET_ERR, - RD_REG_WORD(®->mailbox1), RD_REG_WORD(®->mailbox2), - RD_REG_WORD(®->mailbox3)); + + /* If required, do an MPI FW reset now */ + if (test_and_clear_bit(MPI_RESET_NEEDED, &vha->dpc_flags)) { + if (qla81xx_reset_mpi(vha) != QLA_SUCCESS) { + if (++abts_cnt < 5) { + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + set_bit(MPI_RESET_NEEDED, &vha->dpc_flags); + } + else { + /* + * We exhausted the ISP abort retries. We have to + * set the board offline. + */ + abts_cnt = 0; + vha->flags.online = 0; + } + } + } WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET); RD_REG_DWORD(®->hccr); @@ -578,6 +1017,9 @@ qla24xx_reset_risc(scsi_qla_host_t *ha) } spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (IS_NOPOLLING_TYPE(ha)) + ha->isp_ops->enable_intrs(ha); } /** @@ -587,12 +1029,19 @@ qla24xx_reset_risc(scsi_qla_host_t *ha) * Returns 0 on success. */ void -qla24xx_reset_chip(scsi_qla_host_t *ha) +qla24xx_reset_chip(scsi_qla_host_t *vha) { + struct qla_hw_data *ha = vha->hw; + + if (pci_channel_offline(ha->pdev) && + ha->flags.pci_channel_io_perm_failure) { + return; + } + ha->isp_ops->disable_intrs(ha); /* Perform RISC reset. */ - qla24xx_reset_risc(ha); + qla24xx_reset_risc(vha); } /** @@ -602,20 +1051,22 @@ qla24xx_reset_chip(scsi_qla_host_t *ha) * Returns 0 on success. */ int -qla2x00_chip_diag(scsi_qla_host_t *ha) +qla2x00_chip_diag(scsi_qla_host_t *vha) { int rval; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; unsigned long flags = 0; uint16_t data; uint32_t cnt; uint16_t mb[5]; + struct req_que *req = ha->req_q_map[0]; /* Assume a failed state */ rval = QLA_FUNCTION_FAILED; - DEBUG3(printk(KERN_WARNING "scsi(%ld): Testing device at %p\n", - ha->host_no, ®->flash_address)); + ql_dbg(ql_dbg_init, vha, 0x007b, + "Testing device at %lx.\n", (u_long)®->flash_address); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -633,18 +1084,19 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) * We need to have a delay here since the card will not respond while * in reset causing an MCA on some architectures. */ - udelay(30); + udelay(20); data = qla2x00_debounce_register(®->ctrl_status); - for (cnt = 6000000; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) { + for (cnt = 6000000 ; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) { udelay(5); data = RD_REG_WORD(®->ctrl_status); barrier(); } + if (!cnt) goto chip_diag_failed; - DEBUG3(printk("scsi(%ld): Reset register cleared by chip reset\n", - ha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x007c, + "Reset register cleared by chip reset.\n"); } else { /* * Should be 16 PCI Clock cycles, but this is arbitrarily safer @@ -652,6 +1104,7 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) udelay(100); data = RD_REG_WORD(®->unused_1[0]); WRT_REG_WORD(®->unused_1[0], data&BIT_2); + cnt = 1; } /* Reset RISC processor. */ @@ -666,13 +1119,14 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) data = RD_MAILBOX_REG(ha, reg, 0); barrier(); } - if (!cnt) - goto chip_diag_failed; } else udelay(10); + if (!cnt) + goto chip_diag_failed; + /* Check product ID of chip */ - DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", ha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x007d, "Checking product Id of chip.\n"); mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); @@ -680,8 +1134,9 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) mb[4] = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 4)); if (mb[1] != PROD_ID_1 || (mb[2] != PROD_ID_2 && mb[2] != PROD_ID_2a) || mb[3] != PROD_ID_3) { - qla_printk(KERN_WARNING, ha, - "Wrong product ID = 0x%x,0x%x,0x%x\n", mb[1], mb[2], mb[3]); + ql_log(ql_log_warn, vha, 0x0062, + "Wrong product ID = 0x%x,0x%x,0x%x.\n", + mb[1], mb[2], mb[3]); goto chip_diag_failed; } @@ -691,17 +1146,16 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) ha->product_id[3] = mb[4]; /* Adjust fw RISC transfer size */ - if (ha->request_q_length > 1024) + if (req->length > 1024) ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024; else ha->fw_transfer_size = REQUEST_ENTRY_SIZE * - ha->request_q_length; + req->length; if (IS_QLA2200(ha) && RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) { /* Limit firmware transfer size with a 2200A */ - DEBUG3(printk("scsi(%ld): Found QLA2200A chip.\n", - ha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x007e, "Found QLA2200A Chip.\n"); ha->device_type |= DT_ISP2200A; ha->fw_transfer_size = 128; @@ -710,18 +1164,17 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) /* Wrap Incoming Mailboxes Test. */ spin_unlock_irqrestore(&ha->hardware_lock, flags); - DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", ha->host_no)); - rval = qla2x00_mbx_reg_test(ha); + ql_dbg(ql_dbg_init, vha, 0x007f, "Checking mailboxes.\n"); + rval = qla2x00_mbx_reg_test(vha); if (rval != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, - "Failed mailbox send register test (%x)\n", rval); - + ql_log(ql_log_warn, vha, 0x0080, + "Failed mailbox send register test.\n"); spin_lock_irqsave(&ha->hardware_lock, flags); chip_diag_failed: if (rval != QLA_SUCCESS) - DEBUG2_3(printk("scsi(%ld): Chip diagnostics **** FAILED " - "****\n", ha->host_no)); + ql_log(ql_log_info, vha, 0x0081, + "Chip diagnostics **** FAILED ****.\n"); spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -735,40 +1188,46 @@ chip_diag_failed: * Returns 0 on success. */ int -qla24xx_chip_diag(scsi_qla_host_t *ha) +qla24xx_chip_diag(scsi_qla_host_t *vha) { int rval; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; - /* Perform RISC reset. */ - qla24xx_reset_risc(ha); + if (IS_QLA82XX(ha)) + return QLA_SUCCESS; - ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024; + ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length; - rval = qla2x00_mbx_reg_test(ha); - if (rval != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, - "Failed mailbox send register test (%x)\n", rval); + rval = qla2x00_mbx_reg_test(vha); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x0082, + "Failed mailbox send register test.\n"); + } return rval; } void -qla2x00_alloc_fw_dump(scsi_qla_host_t *ha) +qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) { int rval; uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size, - eft_size, fce_size; + eft_size, fce_size, mq_size; dma_addr_t tc_dma; void *tc; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; if (ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "Firmware dump previously allocated.\n"); + ql_dbg(ql_dbg_init, vha, 0x00bd, + "Firmware dump already allocated.\n"); return; } ha->fw_dumped = 0; - fixed_size = mem_size = eft_size = fce_size = 0; + fixed_size = mem_size = eft_size = fce_size = mq_size = 0; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { fixed_size = sizeof(struct qla2100_fw_dump); } else if (IS_QLA23XX(ha)) { @@ -776,82 +1235,107 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha) mem_size = (ha->fw_memory_size - 0x11000 + 1) * sizeof(uint16_t); } else if (IS_FWI2_CAPABLE(ha)) { - fixed_size = IS_QLA25XX(ha) ? - offsetof(struct qla25xx_fw_dump, ext_mem): - offsetof(struct qla24xx_fw_dump, ext_mem); + if (IS_QLA83XX(ha)) + fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem); + else if (IS_QLA81XX(ha)) + fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem); + else if (IS_QLA25XX(ha)) + fixed_size = offsetof(struct qla25xx_fw_dump, ext_mem); + else + fixed_size = offsetof(struct qla24xx_fw_dump, ext_mem); mem_size = (ha->fw_memory_size - 0x100000 + 1) * sizeof(uint32_t); - - /* Allocate memory for Extended Trace Buffer. */ - tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma, - GFP_KERNEL); - if (!tc) { - qla_printk(KERN_WARNING, ha, "Unable to allocate " - "(%d KB) for EFT.\n", EFT_SIZE / 1024); - goto cont_alloc; + if (ha->mqenable) { + if (!IS_QLA83XX(ha)) + mq_size = sizeof(struct qla2xxx_mq_chain); + /* + * Allocate maximum buffer size for all queues. + * Resizing must be done at end-of-dump processing. + */ + mq_size += ha->max_req_queues * + (req->length * sizeof(request_t)); + mq_size += ha->max_rsp_queues * + (rsp->length * sizeof(response_t)); } - - memset(tc, 0, EFT_SIZE); - rval = qla2x00_enable_eft_trace(ha, tc_dma, EFT_NUM_BUFFERS); - if (rval) { - qla_printk(KERN_WARNING, ha, "Unable to initialize " - "EFT (%d).\n", rval); - dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc, - tc_dma); - goto cont_alloc; - } - - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for EFT...\n", - EFT_SIZE / 1024); - - eft_size = EFT_SIZE; - ha->eft_dma = tc_dma; - ha->eft = tc; - /* Allocate memory for Fibre Channel Event Buffer. */ - if (!IS_QLA25XX(ha)) - goto cont_alloc; + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha)) + goto try_eft; tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma, GFP_KERNEL); if (!tc) { - qla_printk(KERN_WARNING, ha, "Unable to allocate " - "(%d KB) for FCE.\n", FCE_SIZE / 1024); - goto cont_alloc; + ql_log(ql_log_warn, vha, 0x00be, + "Unable to allocate (%d KB) for FCE.\n", + FCE_SIZE / 1024); + goto try_eft; } memset(tc, 0, FCE_SIZE); - rval = qla2x00_enable_fce_trace(ha, tc_dma, FCE_NUM_BUFFERS, + rval = qla2x00_enable_fce_trace(vha, tc_dma, FCE_NUM_BUFFERS, ha->fce_mb, &ha->fce_bufs); if (rval) { - qla_printk(KERN_WARNING, ha, "Unable to initialize " - "FCE (%d).\n", rval); + ql_log(ql_log_warn, vha, 0x00bf, + "Unable to initialize FCE (%d).\n", rval); dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc, tc_dma); ha->flags.fce_enabled = 0; - goto cont_alloc; + goto try_eft; } + ql_dbg(ql_dbg_init, vha, 0x00c0, + "Allocate (%d KB) for FCE...\n", FCE_SIZE / 1024); - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for FCE...\n", - FCE_SIZE / 1024); - - fce_size = sizeof(struct qla2xxx_fce_chain) + EFT_SIZE; + fce_size = sizeof(struct qla2xxx_fce_chain) + FCE_SIZE; ha->flags.fce_enabled = 1; ha->fce_dma = tc_dma; ha->fce = tc; +try_eft: + /* Allocate memory for Extended Trace Buffer. */ + tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma, + GFP_KERNEL); + if (!tc) { + ql_log(ql_log_warn, vha, 0x00c1, + "Unable to allocate (%d KB) for EFT.\n", + EFT_SIZE / 1024); + goto cont_alloc; + } + + memset(tc, 0, EFT_SIZE); + rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS); + if (rval) { + ql_log(ql_log_warn, vha, 0x00c2, + "Unable to initialize EFT (%d).\n", rval); + dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc, + tc_dma); + goto cont_alloc; + } + ql_dbg(ql_dbg_init, vha, 0x00c3, + "Allocated (%d KB) EFT ...\n", EFT_SIZE / 1024); + + eft_size = EFT_SIZE; + ha->eft_dma = tc_dma; + ha->eft = tc; } cont_alloc: - req_q_size = ha->request_q_length * sizeof(request_t); - rsp_q_size = ha->response_q_length * sizeof(response_t); + req_q_size = req->length * sizeof(request_t); + rsp_q_size = rsp->length * sizeof(response_t); dump_size = offsetof(struct qla2xxx_fw_dump, isp); - dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + - eft_size + fce_size; + dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + eft_size; + ha->chain_offset = dump_size; + dump_size += mq_size + fce_size; ha->fw_dump = vmalloc(dump_size); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, "Unable to allocate (%d KB) for " - "firmware dump!!!\n", dump_size / 1024); + ql_log(ql_log_warn, vha, 0x00c4, + "Unable to allocate (%d KB) for firmware dump.\n", + dump_size / 1024); + + if (ha->fce) { + dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, + ha->fce_dma); + ha->fce = NULL; + ha->fce_dma = 0; + } if (ha->eft) { dma_free_coherent(&ha->pdev->dev, eft_size, ha->eft, @@ -861,9 +1345,8 @@ cont_alloc: } return; } - - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for firmware dump...\n", - dump_size / 1024); + ql_dbg(ql_dbg_init, vha, 0x00c5, + "Allocated (%d KB) for firmware dump.\n", dump_size / 1024); ha->fw_dump_len = dump_size; ha->fw_dump->signature[0] = 'Q'; @@ -885,58 +1368,51 @@ cont_alloc: htonl(offsetof(struct qla2xxx_fw_dump, isp)); } -/** - * qla2x00_resize_request_q() - Resize request queue given available ISP memory. - * @ha: HA context - * - * Returns 0 on success. - */ -static void -qla2x00_resize_request_q(scsi_qla_host_t *ha) +static int +qla81xx_mpi_sync(scsi_qla_host_t *vha) { +#define MPS_MASK 0xe0 int rval; - uint16_t fw_iocb_cnt = 0; - uint16_t request_q_length = REQUEST_ENTRY_CNT_2XXX_EXT_MEM; - dma_addr_t request_dma; - request_t *request_ring; + uint16_t dc; + uint32_t dw; - /* Valid only on recent ISPs. */ - if (IS_QLA2100(ha) || IS_QLA2200(ha)) - return; + if (!IS_QLA81XX(vha->hw)) + return QLA_SUCCESS; - /* Retrieve IOCB counts available to the firmware. */ - rval = qla2x00_get_resource_cnts(ha, NULL, NULL, NULL, &fw_iocb_cnt, - &ha->max_npiv_vports); - if (rval) - return; - /* No point in continuing if current settings are sufficient. */ - if (fw_iocb_cnt < 1024) - return; - if (ha->request_q_length >= request_q_length) - return; + rval = qla2x00_write_ram_word(vha, 0x7c00, 1); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x0105, + "Unable to acquire semaphore.\n"); + goto done; + } - /* Attempt to claim larger area for request queue. */ - request_ring = dma_alloc_coherent(&ha->pdev->dev, - (request_q_length + 1) * sizeof(request_t), &request_dma, - GFP_KERNEL); - if (request_ring == NULL) - return; + pci_read_config_word(vha->hw->pdev, 0x54, &dc); + rval = qla2x00_read_ram_word(vha, 0x7a15, &dw); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x0067, "Unable to read sync.\n"); + goto done_release; + } - /* Resize successful, report extensions. */ - qla_printk(KERN_INFO, ha, "Extended memory detected (%d KB)...\n", - (ha->fw_memory_size + 1) / 1024); - qla_printk(KERN_INFO, ha, "Resizing request queue depth " - "(%d -> %d)...\n", ha->request_q_length, request_q_length); + dc &= MPS_MASK; + if (dc == (dw & MPS_MASK)) + goto done_release; - /* Clear old allocations. */ - dma_free_coherent(&ha->pdev->dev, - (ha->request_q_length + 1) * sizeof(request_t), ha->request_ring, - ha->request_dma); + dw &= ~MPS_MASK; + dw |= dc; + rval = qla2x00_write_ram_word(vha, 0x7a15, dw); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x0114, "Unable to gain sync.\n"); + } - /* Begin using larger queue. */ - ha->request_q_length = request_q_length; - ha->request_ring = request_ring; - ha->request_dma = request_dma; +done_release: + rval = qla2x00_write_ram_word(vha, 0x7c00, 0); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x006d, + "Unable to release semaphore.\n"); + } + +done: + return rval; } /** @@ -946,12 +1422,23 @@ qla2x00_resize_request_q(scsi_qla_host_t *ha) * Returns 0 on success. */ static int -qla2x00_setup_chip(scsi_qla_host_t *ha) +qla2x00_setup_chip(scsi_qla_host_t *vha) { int rval; uint32_t srisc_address = 0; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; unsigned long flags; + uint16_t fw_major_version; + + if (IS_QLA82XX(ha)) { + rval = ha->isp_ops->load_risc(vha, &srisc_address); + if (rval == QLA_SUCCESS) { + qla2x00_stop_firmware(vha); + goto enable_82xx_npiv; + } else + goto failed; + } if (!IS_FWI2_CAPABLE(ha) && !IS_QLA2100(ha) && !IS_QLA2200(ha)) { /* Disable SRAM, Instruction RAM and GP RAM parity. */ @@ -961,30 +1448,34 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } - /* Load firmware sequences */ - rval = ha->isp_ops->load_risc(ha, &srisc_address); - if (rval == QLA_SUCCESS) { - DEBUG(printk("scsi(%ld): Verifying Checksum of loaded RISC " - "code.\n", ha->host_no)); + qla81xx_mpi_sync(vha); - rval = qla2x00_verify_checksum(ha, srisc_address); + /* Load firmware sequences */ + rval = ha->isp_ops->load_risc(vha, &srisc_address); + if (rval == QLA_SUCCESS) { + ql_dbg(ql_dbg_init, vha, 0x00c9, + "Verifying Checksum of loaded RISC code.\n"); + + rval = qla2x00_verify_checksum(vha, srisc_address); if (rval == QLA_SUCCESS) { /* Start firmware execution. */ - DEBUG(printk("scsi(%ld): Checksum OK, start " - "firmware.\n", ha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x00ca, + "Starting firmware.\n"); - rval = qla2x00_execute_fw(ha, srisc_address); + rval = qla2x00_execute_fw(vha, srisc_address); /* Retrieve firmware information. */ - if (rval == QLA_SUCCESS && ha->fw_major_version == 0) { - qla2x00_get_fw_version(ha, - &ha->fw_major_version, - &ha->fw_minor_version, - &ha->fw_subminor_version, - &ha->fw_attributes, &ha->fw_memory_size); - qla2x00_resize_request_q(ha); + if (rval == QLA_SUCCESS) { +enable_82xx_npiv: + fw_major_version = ha->fw_major_version; + if (IS_QLA82XX(ha)) + qla82xx_check_md_needed(vha); + else + rval = qla2x00_get_fw_version(vha); + if (rval != QLA_SUCCESS) + goto failed; ha->flags.npiv_supported = 0; - if ((IS_QLA24XX(ha) || IS_QLA25XX(ha)) && - (ha->fw_attributes & BIT_2)) { + if (IS_QLA2XXX_MIDTYPE(ha) && + (ha->fw_attributes & BIT_2)) { ha->flags.npiv_supported = 1; if ((!ha->max_npiv_vports) || ((ha->max_npiv_vports + 1) % @@ -992,14 +1483,18 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1; } + qla2x00_get_resource_cnts(vha, NULL, + &ha->fw_xcb_count, NULL, NULL, + &ha->max_npiv_vports, NULL); - if (ql2xallocfwdump) - qla2x00_alloc_fw_dump(ha); + if (!fw_major_version && ql2xallocfwdump + && !IS_QLA82XX(ha)) + qla2x00_alloc_fw_dump(vha); } } else { - DEBUG2(printk(KERN_INFO - "scsi(%ld): ISP Firmware failed checksum.\n", - ha->host_no)); + ql_log(ql_log_fatal, vha, 0x00cd, + "ISP Firmware failed checksum.\n"); + goto failed; } } @@ -1016,9 +1511,41 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } + // if SR-SRIOV enabled firmware + if (IS_QLA83XX(ha) && (ha->fw_attributes_h & BIT_1)) { + rval = qla83xx_configure_vfs(vha); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_vport, vha, 0xa008, + "ISP Firmware failed to configure VFs.\n"); + } + } + + if (IS_QLA83XX(ha)) + goto skip_fac_check; + + if (rval == QLA_SUCCESS && IS_FAC_REQUIRED(ha)) { + uint32_t size; + + rval = qla81xx_fac_get_sector_size(vha, &size); + if (rval == QLA_SUCCESS) { + ha->flags.fac_supported = 1; + ha->fdt_block_size = size << 2; + } else { + ql_log(ql_log_warn, vha, 0x00ce, + "Unsupported FAC firmware (%d.%02d.%02d).\n", + ha->fw_major_version, ha->fw_minor_version, + ha->fw_subminor_version); +skip_fac_check: + if (IS_QLA83XX(ha)) { + ha->flags.fac_supported = 0; + rval = QLA_SUCCESS; + } + } + } +failed: if (rval != QLA_SUCCESS) { - DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n", - ha->host_no)); + ql_log(ql_log_fatal, vha, 0x00cf, + "Setup chip ****FAILED****.\n"); } return (rval); @@ -1033,14 +1560,17 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) * * Returns 0 on success. */ -static void -qla2x00_init_response_q_entries(scsi_qla_host_t *ha) +void +qla2x00_init_response_q_entries(struct rsp_que *rsp) { uint16_t cnt; response_t *pkt; - pkt = ha->response_ring_ptr; - for (cnt = 0; cnt < ha->response_q_length; cnt++) { + rsp->ring_ptr = rsp->ring; + rsp->ring_index = 0; + rsp->status_srb = NULL; + pkt = rsp->ring_ptr; + for (cnt = 0; cnt < rsp->length; cnt++) { pkt->signature = RESPONSE_PROCESSED; pkt++; } @@ -1050,7 +1580,7 @@ qla2x00_init_response_q_entries(scsi_qla_host_t *ha) #ifdef CONFIG_SCSI_QLA2XXX_TARGET /** * qla2x00_init_atio_q_entries() - Initializes ATIO queue entries. - * @ha: HA context + * @vha: VHA context * * Beginning of ATIO ring has initialization control block already built * by nvram config routine. @@ -1058,13 +1588,13 @@ qla2x00_init_response_q_entries(scsi_qla_host_t *ha) * Returns 0 on success. */ static void -qla2x00_init_atio_q_entries(scsi_qla_host_t *ha) +qla2x00_init_atio_q_entries(scsi_qla_host_t *vha) { uint16_t cnt; atio_t *pkt; - pkt = ha->atio_ring; - for (cnt = 0; cnt < ha->atio_q_length; cnt++) { + pkt = vha->hw->atio_ring; + for (cnt = 0; cnt < vha->hw->atio_q_length; cnt++) { pkt->signature = ATIO_PROCESSED; pkt++; } @@ -1079,21 +1609,23 @@ qla2x00_init_atio_q_entries(scsi_qla_host_t *ha) * Returns 0 on success. */ void -qla2x00_update_fw_options(scsi_qla_host_t *ha) +qla2x00_update_fw_options(scsi_qla_host_t *vha) { uint16_t swing, emphasis, tx_sens, rx_sens; + struct qla_hw_data *ha = vha->hw; memset(ha->fw_options, 0, sizeof(ha->fw_options)); - qla2x00_get_fw_options(ha, ha->fw_options); + qla2x00_get_fw_options(vha, ha->fw_options); if (IS_QLA2100(ha) || IS_QLA2200(ha)) return; /* Serial Link options. */ - DEBUG3(printk("scsi(%ld): Serial link options:\n", - ha->host_no)); - DEBUG3(qla2x00_dump_buffer((uint8_t *)&ha->fw_seriallink_options, - sizeof(ha->fw_seriallink_options))); + ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0115, + "Serial link options.\n"); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0109, + (uint8_t *)&ha->fw_seriallink_options, + sizeof(ha->fw_seriallink_options)); ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING; if (ha->fw_seriallink_options[3] & BIT_2) { @@ -1149,42 +1681,49 @@ qla2x00_update_fw_options(scsi_qla_host_t *ha) ha->fw_options[2] |= BIT_13; /* Update firmware options. */ - qla2x00_set_fw_options(ha, ha->fw_options); + qla2x00_set_fw_options(vha, ha->fw_options); } void -qla24xx_update_fw_options(scsi_qla_host_t *ha) +qla24xx_update_fw_options(scsi_qla_host_t *vha) { int rval; + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA82XX(ha)) + return; /* Update Serial Link options. */ if ((le16_to_cpu(ha->fw_seriallink_options24[0]) & BIT_0) == 0) return; - rval = qla2x00_set_serdes_params(ha, + rval = qla2x00_set_serdes_params(vha, le16_to_cpu(ha->fw_seriallink_options24[1]), le16_to_cpu(ha->fw_seriallink_options24[2]), le16_to_cpu(ha->fw_seriallink_options24[3])); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x0104, "Unable to update Serial Link options (%x).\n", rval); } } void -qla2x00_config_rings(struct scsi_qla_host *ha) +qla2x00_config_rings(struct scsi_qla_host *vha) { + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; /* Setup ring parameters in initialization control block. */ ha->init_cb->request_q_outpointer = __constant_cpu_to_le16(0); ha->init_cb->response_q_inpointer = __constant_cpu_to_le16(0); - ha->init_cb->request_q_length = cpu_to_le16(ha->request_q_length); - ha->init_cb->response_q_length = cpu_to_le16(ha->response_q_length); - ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(ha->request_dma)); - ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(ha->request_dma)); - ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma)); - ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma)); + ha->init_cb->request_q_length = cpu_to_le16(req->length); + ha->init_cb->response_q_length = cpu_to_le16(rsp->length); + ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); + ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(req->dma)); + ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma)); + ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma)); WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), 0); WRT_REG_WORD(ISP_REQ_Q_OUT(ha, reg), 0); @@ -1194,40 +1733,96 @@ qla2x00_config_rings(struct scsi_qla_host *ha) } void -qla24xx_config_rings(struct scsi_qla_host *ha) +qla24xx_config_rings(struct scsi_qla_host *vha) { - struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + struct qla_hw_data *ha = vha->hw; + device_reg_t __iomem *reg = ISP_QUE_REG(ha, 0); + struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp; + struct qla_msix_entry *msix; struct init_cb_24xx *icb; + uint16_t rid = 0; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; /* Setup ring parameters in initialization control block. */ icb = (struct init_cb_24xx *)ha->init_cb; icb->request_q_outpointer = __constant_cpu_to_le16(0); icb->response_q_inpointer = __constant_cpu_to_le16(0); - icb->request_q_length = cpu_to_le16(ha->request_q_length); - icb->response_q_length = cpu_to_le16(ha->response_q_length); - icb->request_q_address[0] = cpu_to_le32(LSD(ha->request_dma)); - icb->request_q_address[1] = cpu_to_le32(MSD(ha->request_dma)); - icb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma)); - icb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma)); + icb->request_q_length = cpu_to_le16(req->length); + icb->response_q_length = cpu_to_le16(rsp->length); + icb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); + icb->request_q_address[1] = cpu_to_le32(MSD(req->dma)); + icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma)); + icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma)); #ifdef CONFIG_SCSI_QLA2XXX_TARGET icb->atio_q_inpointer = __constant_cpu_to_le16(0); - icb->atio_q_length = cpu_to_le16(ha->atio_q_length); - icb->atio_q_address[0] = cpu_to_le32(LSD(ha->atio_dma)); - icb->atio_q_address[1] = cpu_to_le32(MSD(ha->atio_dma)); + icb->atio_q_length = cpu_to_le16(vha->hw->atio_q_length); + icb->atio_q_address[0] = cpu_to_le32(LSD(vha->hw->atio_dma)); + icb->atio_q_address[1] = cpu_to_le32(MSD(vha->hw->atio_dma)); #endif - WRT_REG_DWORD(®->req_q_in, 0); - WRT_REG_DWORD(®->req_q_out, 0); - WRT_REG_DWORD(®->rsp_q_in, 0); - WRT_REG_DWORD(®->rsp_q_out, 0); - RD_REG_DWORD(®->rsp_q_out); + if (ha->mqenable || IS_QLA83XX(ha)) { + icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS); + icb->rid = __constant_cpu_to_le16(rid); + if (ha->flags.msix_enabled) { + msix = &ha->msix_entries[1]; + ql_dbg(ql_dbg_init, vha, 0x00fd, + "Registering vector 0x%x for base que.\n", + msix->entry); + icb->msix = cpu_to_le16(msix->entry); + } + /* Use alternate PCI bus number */ + if (MSB(rid)) + icb->firmware_options_2 |= + __constant_cpu_to_le32(BIT_19); + /* Use alternate PCI devfn */ + if (LSB(rid)) + icb->firmware_options_2 |= + __constant_cpu_to_le32(BIT_18); #ifdef CONFIG_SCSI_QLA2XXX_TARGET - WRT_REG_DWORD(®->atio_q_in, 0); - WRT_REG_DWORD(®->atio_q_out, 0); - RD_REG_DWORD(®->atio_q_out); + if (IS_ATIO_MSIX_CAPABLE(ha)) { + msix = &ha->msix_entries[2]; + icb->msix_atio = cpu_to_le16(msix->entry); + ql_dbg(ql_dbg_init, vha, 0xffff, + "Registering ICB vector 0x%x for atio que.\n", + msix->entry); + } #endif + /* Use Disable MSIX Handshake mode for capable adapters */ + if ((ha->fw_attributes & BIT_6) && (IS_MSIX_NACK_CAPABLE(ha)) && + (ha->flags.msix_enabled)) { + icb->firmware_options_2 &= + __constant_cpu_to_le32(~BIT_22); + ha->disable_msix_handshake = 1; + ql_dbg(ql_dbg_init, vha, 0x00fe, + "MSIX Handshake Disable Mode turned on.\n"); + } else { + icb->firmware_options_2 |= + __constant_cpu_to_le32(BIT_22); + } + icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_23); + + WRT_REG_DWORD(®->isp25mq.req_q_in, 0); + WRT_REG_DWORD(®->isp25mq.req_q_out, 0); + WRT_REG_DWORD(®->isp25mq.rsp_q_in, 0); + WRT_REG_DWORD(®->isp25mq.rsp_q_out, 0); + } else { + WRT_REG_DWORD(®->isp24.req_q_in, 0); + WRT_REG_DWORD(®->isp24.req_q_out, 0); + WRT_REG_DWORD(®->isp24.rsp_q_in, 0); + WRT_REG_DWORD(®->isp24.rsp_q_out, 0); + } + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + WRT_REG_DWORD(ISP_ATIO_Q_IN(vha), 0); + WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), 0); + RD_REG_DWORD(ISP_ATIO_Q_OUT(vha)); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + + /* PCI posting */ + RD_REG_DWORD(&ioreg->hccr); } /** @@ -1240,63 +1835,80 @@ qla24xx_config_rings(struct scsi_qla_host *ha) * Returns 0 on success. */ static int -qla2x00_init_rings(scsi_qla_host_t *ha) +qla2x00_init_rings(scsi_qla_host_t *vha) { int rval; unsigned long flags = 0; - int cnt; + int cnt, que; + struct qla_hw_data *ha = vha->hw; + struct req_que *req; + struct rsp_que *rsp; struct mid_init_cb_24xx *mid_init_cb = (struct mid_init_cb_24xx *) ha->init_cb; spin_lock_irqsave(&ha->hardware_lock, flags); /* Clear outstanding commands array. */ - for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) - ha->outstanding_cmds[cnt] = NULL; + for (que = 0; que < ha->max_req_queues; que++) { + req = ha->req_q_map[que]; + if (!req) + continue; + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) + req->outstanding_cmds[cnt] = NULL; - ha->current_outstanding_cmd = 0; + req->current_outstanding_cmd = 1; - /* Clear RSCN queue. */ - ha->rscn_in_ptr = 0; - ha->rscn_out_ptr = 0; - - /* Initialize firmware. */ - ha->request_ring_ptr = ha->request_ring; - ha->req_ring_index = 0; - ha->req_q_cnt = ha->request_q_length; - ha->response_ring_ptr = ha->response_ring; - ha->rsp_ring_index = 0; - - /* Initialize response queue entries */ - qla2x00_init_response_q_entries(ha); + /* Initialize firmware. */ + req->ring_ptr = req->ring; + req->ring_index = 0; + req->cnt = req->length; + } + for (que = 0; que < ha->max_rsp_queues; que++) { + rsp = ha->rsp_q_map[que]; + if (!rsp) + continue; + /* Initialize response queue entries */ + qla2x00_init_response_q_entries(rsp); + } #ifdef CONFIG_SCSI_QLA2XXX_TARGET - ha->atio_ring_ptr = ha->atio_ring; - ha->atio_ring_index = 0; - qla2x00_init_atio_q_entries(ha); /* Initialize ATIO queue entries */ -#endif + vha->hw->atio_ring_ptr = vha->hw->atio_ring; + vha->hw->atio_ring_index = 0; + qla2x00_init_atio_q_entries(vha); /* Initialize ATIO queue entries */ +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - ha->isp_ops->config_rings(ha); + spin_lock(&ha->vport_slock); + + spin_unlock(&ha->vport_slock); + + ha->isp_ops->config_rings(vha); spin_unlock_irqrestore(&ha->hardware_lock, flags); /* Update any ISP specific firmware options before initialization. */ - ha->isp_ops->update_fw_options(ha); + ha->isp_ops->update_fw_options(vha); - DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x00d1, "Issue init firmware.\n"); - if (ha->flags.npiv_supported) + if (ha->flags.npiv_supported) { + if (ha->operating_mode == LOOP) + ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1; mid_init_cb->count = cpu_to_le16(ha->max_npiv_vports); + } - mid_init_cb->options = __constant_cpu_to_le16(BIT_1); + if (IS_FWI2_CAPABLE(ha)) { + mid_init_cb->options = __constant_cpu_to_le16(BIT_1); + mid_init_cb->init_cb.execution_throttle = + cpu_to_le16(ha->fw_xcb_count); + } - rval = qla2x00_init_firmware(ha, ha->init_cb_size); + rval = qla2x00_init_firmware(vha, ha->init_cb_size); if (rval != QLA_SUCCESS) { - DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n", - ha->host_no)); + ql_log(ql_log_fatal, vha, 0x00d2, + "Init Firmware **** FAILED ****.\n"); } else { - DEBUG3(printk("scsi(%ld): Init firmware -- success.\n", - ha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x00d3, + "Init Firmware -- success.\n"); } return (rval); @@ -1309,13 +1921,19 @@ qla2x00_init_rings(scsi_qla_host_t *ha) * Returns 0 on success. */ static int -qla2x00_fw_ready(scsi_qla_host_t *ha) +qla2x00_fw_ready(scsi_qla_host_t *vha) { int rval; unsigned long wtime, mtime, cs84xx_time; uint16_t min_wait; /* Minimum wait time if loop is down */ uint16_t wait_time; /* Wait time if loop is coming ready */ - uint16_t state[3]; + uint16_t state[5]; + struct qla_hw_data *ha = vha->hw; + + if (!qla_firmware_active(vha)) { + rval = QLA_FUNCTION_FAILED; + goto out_not_ready; + } rval = QLA_SUCCESS; @@ -1337,45 +1955,48 @@ qla2x00_fw_ready(scsi_qla_host_t *ha) wtime = jiffies + (wait_time * HZ); /* Wait for ISP to finish LIP */ - if (!ha->flags.init_done) - qla_printk(KERN_INFO, ha, "Waiting for LIP to complete...\n"); - - DEBUG3(printk("scsi(%ld): Waiting for LIP to complete...\n", - ha->host_no)); + if (!vha->flags.init_done) + ql_log(ql_log_info, vha, 0x801e, + "Waiting for LIP to complete.\n"); do { - rval = qla2x00_get_firmware_state(ha, state); + rval = qla2x00_get_firmware_state(vha, state); if (rval == QLA_SUCCESS) { if (state[0] < FSTATE_LOSS_OF_SYNC) { - ha->device_flags &= ~DFLG_NO_CABLE; + vha->device_flags &= ~DFLG_NO_CABLE; } if (IS_QLA84XX(ha) && state[0] != FSTATE_READY) { - DEBUG16(printk("scsi(%ld): fw_state=%x " - "84xx=%x.\n", ha->host_no, state[0], - state[2])); + ql_dbg(ql_dbg_taskm, vha, 0x801f, + "fw_state=%x 84xx=%x.\n", state[0], + state[2]); if ((state[2] & FSTATE_LOGGED_IN) && (state[2] & FSTATE_WAITING_FOR_VERIFY)) { - DEBUG16(printk("scsi(%ld): Sending " - "verify iocb.\n", ha->host_no)); + ql_dbg(ql_dbg_taskm, vha, 0x8028, + "Sending verify iocb.\n"); cs84xx_time = jiffies; - rval = qla84xx_init_chip(ha); - if (rval != QLA_SUCCESS) + rval = qla84xx_init_chip(vha); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, + vha, 0x8007, + "Init chip failed.\n"); break; + } /* Add time taken to initialize. */ cs84xx_time = jiffies - cs84xx_time; wtime += cs84xx_time; mtime += cs84xx_time; - DEBUG16(printk("scsi(%ld): Increasing " - "wait time by %ld. New time %ld\n", - ha->host_no, cs84xx_time, wtime)); + ql_dbg(ql_dbg_taskm, vha, 0x8008, + "Increasing wait time by %ld. " + "New time %ld.\n", cs84xx_time, + wtime); } } else if (state[0] == FSTATE_READY) { - DEBUG(printk("scsi(%ld): F/W Ready - OK \n", - ha->host_no)); + ql_dbg(ql_dbg_taskm, vha, 0x8037, + "F/W Ready - OK.\n"); - qla2x00_get_retry_cnt(ha, &ha->retry_count, + qla2x00_get_retry_cnt(vha, &ha->retry_count, &ha->login_timeout, &ha->r_a_tov); rval = QLA_SUCCESS; @@ -1384,22 +2005,23 @@ qla2x00_fw_ready(scsi_qla_host_t *ha) rval = QLA_FUNCTION_FAILED; - if (atomic_read(&ha->loop_down_timer) && + if (atomic_read(&vha->loop_down_timer) && state[0] != FSTATE_READY) { /* Loop down. Timeout on min_wait for states * other than Wait for Login. */ if (time_after_eq(jiffies, mtime)) { - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x8038, "Cable is unplugged...\n"); - ha->device_flags |= DFLG_NO_CABLE; + vha->device_flags |= DFLG_NO_CABLE; break; } } } else { /* Mailbox cmd failed. Timeout on min_wait. */ - if (time_after_eq(jiffies, mtime)) + if (time_after_eq(jiffies, mtime) || + ha->flags.isp82xx_fw_hung) break; } @@ -1408,18 +2030,15 @@ qla2x00_fw_ready(scsi_qla_host_t *ha) /* Delay for a while */ msleep(500); - - DEBUG3(printk("scsi(%ld): fw_state=%x curr time=%lx.\n", - ha->host_no, state[0], jiffies)); } while (1); - DEBUG(printk("scsi(%ld): fw_state=%x curr time=%lx.\n", - ha->host_no, state[0], jiffies)); + ql_dbg(ql_dbg_taskm, vha, 0x803a, + "fw_state=%x (%x, %x, %x, %x) " "curr time=%lx.\n", state[0], + state[1], state[2], state[3], state[4], jiffies); - if (rval != QLA_SUCCESS) { - DEBUG2_3(printk(KERN_INFO "scsi(%ld): Firmware not ready " - "(%x).\n", ha->host_no, rval)); - } +out_not_ready: + if ((rval != QLA_SUCCESS) && !(vha->device_flags & DFLG_NO_CABLE)) + ql_dbg(ql_log_warn, vha, 0x803b, "Firmware not ready\n"); return (rval); } @@ -1438,7 +2057,7 @@ qla2x00_fw_ready(scsi_qla_host_t *ha) * Kernel context. */ static int -qla2x00_configure_hba(scsi_qla_host_t *ha) +qla2x00_configure_hba(scsi_qla_host_t *vha) { int rval; uint16_t loop_id; @@ -1448,65 +2067,61 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) uint8_t area; uint8_t domain; char connect_type[22]; -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - scsi_qla_host_t *pha = ha->parent; -#endif + struct qla_hw_data *ha = vha->hw; + /* Get host addresses. */ - rval = qla2x00_get_adapter_id(ha, + rval = qla2x00_get_adapter_id(vha, &loop_id, &al_pa, &area, &domain, &topo, &sw_cap); if (rval != QLA_SUCCESS) { - if (LOOP_TRANSITION(ha) || atomic_read(&ha->loop_down_timer) || + if (LOOP_TRANSITION(vha) || atomic_read(&ha->loop_down_timer) || + IS_CNA_CAPABLE(ha) || (rval == QLA_COMMAND_ERROR && loop_id == 0x7)) { - DEBUG2(printk("%s(%ld) Loop is in a transition state\n", - __func__, ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2008, + "Loop is in a transition state.\n"); } else { - qla_printk(KERN_WARNING, ha, - "ERROR -- Unable to get host loop ID.\n"); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ql_log(ql_log_warn, vha, 0x2009, + "Unable to get host loop ID.\n"); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); } return (rval); } if (topo == 4) { - qla_printk(KERN_INFO, ha, - "Cannot get topology - retrying.\n"); + ql_log(ql_log_info, vha, 0x200a, + "Cannot get topology - retrying.\n"); return (QLA_FUNCTION_FAILED); } - ha->loop_id = loop_id; + vha->loop_id = loop_id; /* initialize */ - ha->min_external_loopid = SNS_FIRST_LOOP_ID+1; + ha->min_external_loopid = SNS_FIRST_LOOP_ID; ha->operating_mode = LOOP; ha->switch_cap = 0; switch (topo) { case 0: - DEBUG3(printk("scsi(%ld): HBA in NL topology.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x200b, "HBA in NL topology.\n"); ha->current_topology = ISP_CFG_NL; strcpy(connect_type, "(Loop)"); break; case 1: - DEBUG3(printk("scsi(%ld): HBA in FL topology.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x200c, "HBA in FL topology.\n"); ha->switch_cap = sw_cap; ha->current_topology = ISP_CFG_FL; strcpy(connect_type, "(FL_Port)"); break; case 2: - DEBUG3(printk("scsi(%ld): HBA in N P2P topology.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x200d, "HBA in N P2P topology.\n"); ha->operating_mode = P2P; ha->current_topology = ISP_CFG_N; strcpy(connect_type, "(N_Port-to-N_Port)"); break; case 3: - DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x200e, "HBA in F P2P topology.\n"); ha->switch_cap = sw_cap; ha->operating_mode = P2P; ha->current_topology = ISP_CFG_F; @@ -1514,9 +2129,8 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) break; default: - DEBUG3(printk("scsi(%ld): HBA in unknown topology %x. " - "Using NL.\n", - ha->host_no, topo)); + ql_dbg(ql_dbg_disc, vha, 0x200f, + "HBA in unknown topology %x, using NL.\n", topo); ha->current_topology = ISP_CFG_NL; strcpy(connect_type, "(Loop)"); break; @@ -1524,32 +2138,33 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) /* Save Host port and loop ID. */ /* byte order - Big Endian */ - ha->d_id.b.domain = domain; - ha->d_id.b.area = area; - ha->d_id.b.al_pa = al_pa; -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (pha) - pha->tgt_vp_map[al_pa].idx = ha->vp_idx; -#endif - if (!ha->flags.init_done) - qla_printk(KERN_INFO, ha, - "Topology - %s, Host Loop address 0x%x\n", - connect_type, ha->loop_id); + vha->d_id.b.domain = domain; + vha->d_id.b.area = area; + vha->d_id.b.al_pa = al_pa; - if (rval) { - DEBUG2_3(printk("scsi(%ld): FAILED.\n", ha->host_no)); - } else { - DEBUG3(printk("scsi(%ld): exiting normally.\n", ha->host_no)); - } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (IS_FWI2_CAPABLE(ha)) + ha->tgt_vp_map[al_pa].idx = vha->vp_idx; +#endif + if (!vha->flags.init_done) + ql_log(ql_log_info, vha, 0x2010, + "Topology - %s, Host Loop address 0x%x.\n", + connect_type, vha->loop_id); + + ql_dbg(ql_dbg_disc, vha, 0x2012, "%s success\n", __func__); return(rval); } -static inline void -qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *def) +inline void +qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len, + char *def) { char *st, *en; uint16_t index; + struct qla_hw_data *ha = vha->hw; + int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha); if (memcmp(model, BINZERO, len) != 0) { strncpy(ha->model_number, model, len); @@ -1562,28 +2177,38 @@ qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *de } index = (ha->pdev->subsystem_device & 0xff); - if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && + if (use_tbl && + ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && index < QLA_MODEL_NAMES) - ha->model_desc = qla2x00_model_name[index * 2 + 1]; + strncpy(ha->model_desc, + qla2x00_model_name[index * 2 + 1], + sizeof(ha->model_desc) - 1); } else { index = (ha->pdev->subsystem_device & 0xff); - if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && + if (use_tbl && + ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && index < QLA_MODEL_NAMES) { strcpy(ha->model_number, qla2x00_model_name[index * 2]); - ha->model_desc = qla2x00_model_name[index * 2 + 1]; + strncpy(ha->model_desc, + qla2x00_model_name[index * 2 + 1], + sizeof(ha->model_desc) - 1); } else { strcpy(ha->model_number, def); } } + if (IS_FWI2_CAPABLE(ha)) + qla2xxx_get_vpd_field(vha, "\x82", ha->model_desc, + sizeof(ha->model_desc)); } /* On sparc systems, obtain port and node WWN from firmware * properties. */ -static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, nvram_t *nv) +static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *vha, nvram_t *nv) { #ifdef CONFIG_SPARC + struct qla_hw_data *ha = vha->hw; struct pci_dev *pdev = ha->pdev; struct device_node *dp = pci_device_to_OF_node(pdev); const u8 *val; @@ -1613,12 +2238,13 @@ static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, nvram_t *nv) * 0 = success. */ int -qla2x00_nvram_config(scsi_qla_host_t *ha) +qla2x00_nvram_config(scsi_qla_host_t *vha) { int rval; uint8_t chksum = 0; uint16_t cnt; uint8_t *dptr1, *dptr2; + struct qla_hw_data *ha = vha->hw; init_cb_t *icb = ha->init_cb; nvram_t *nv = ha->nvram; uint8_t *ptr = ha->nvram; @@ -1626,13 +2252,6 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) rval = QLA_SUCCESS; - if (unlikely(nv == NULL)) { - qla_printk(KERN_ERR, ha, "request_ring pointer is NULL\n"); - dump_stack(); - rval = 1; - goto out; - } - /* Determine NVRAM starting address. */ ha->nvram_size = sizeof(nvram_t); ha->nvram_base = 0; @@ -1641,26 +2260,26 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) ha->nvram_base = 0x80; /* Get NVRAM data and calculate checksum. */ - ha->isp_ops->read_nvram(ha, ptr, ha->nvram_base, ha->nvram_size); + ha->isp_ops->read_nvram(vha, ptr, ha->nvram_base, ha->nvram_size); for (cnt = 0, chksum = 0; cnt < ha->nvram_size; cnt++) chksum += *ptr++; - DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size)); + ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010f, + "Contents of NVRAM.\n"); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0110, + (uint8_t *)nv, ha->nvram_size); /* Bad NVRAM data, set defaults parameters. */ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) { /* Reset NVRAM data. */ - qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: " - "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0], - nv->nvram_version); - qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " - "invalid -- WWPN) defaults.\n"); - - if (chksum) - qla2xxx_hw_event_log(ha, HW_EVENT_NVRAM_CHKSUM_ERR, 0, - MSW(chksum), LSW(chksum)); + ql_log(ql_log_warn, vha, 0x0064, + "Inconsistent NVRAM " + "detected: checksum=0x%x id=%c version=0x%x.\n", + chksum, nv->id[0], nv->nvram_version); + ql_log(ql_log_warn, vha, 0x0065, + "Falling back to " + "functioning (yet invalid -- WWPN) defaults.\n"); /* * Set default initialization control block. @@ -1696,7 +2315,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) nv->port_name[3] = 224; nv->port_name[4] = 139; - qla2xxx_nvram_wwn_from_ofw(ha, nv); + qla2xxx_nvram_wwn_from_ofw(vha, nv); nv->login_timeout = 4; @@ -1733,15 +2352,19 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) * Setup driver NVRAM options. */ #ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (!IS_QLA2100(ha)) { + if (!IS_QLA2100(vha->hw)) { /* Check if target mode enabled */ - if (qla_tgt_mode_enabled(ha)) { + if (qla_tgt_mode_enabled(vha)) { if (!ha->saved_set) { /* We save only once */ - ha->saved_firmware_options[0] = nv->firmware_options[0]; - ha->saved_firmware_options[1] = nv->firmware_options[1]; - ha->saved_add_firmware_options[0] = nv->add_firmware_options[0]; - ha->saved_add_firmware_options[1] = nv->add_firmware_options[1]; + ha->saved_firmware_options[0] = + nv->firmware_options[0]; + ha->saved_firmware_options[1] = + nv->firmware_options[1]; + ha->saved_add_firmware_options[0] = + nv->add_firmware_options[0]; + ha->saved_add_firmware_options[1] = + nv->add_firmware_options[1]; ha->saved_set = 1; } @@ -1749,38 +2372,54 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) nv->firmware_options[0] |= BIT_4; /* Disable ini mode, if requested */ - if (!qla_ini_mode_enabled(ha)) + if (!qla_ini_mode_enabled(vha)) nv->firmware_options[0] |= BIT_5; - /* Disable Full Login after LIP */ - nv->firmware_options[1] &= ~BIT_5; + if (ha->enable_explicit_conf) { + ql_log(ql_log_info, vha, 0x1073, "Enabling FC tape support\n"); + /* Disable Full Login after LIP */ + nv->firmware_options[1] &= ~BIT_5; + /* Enable FC tapes support */ + nv->add_firmware_options[1] |= BIT_4; + /* Enable Fibre Channel Confirm */ + nv->add_firmware_options[1] |= BIT_5; + /* Enable Command Queuing in Target Mode */ + nv->add_firmware_options[1] |= BIT_6; + } else { + ql_log(ql_log_info, vha, 0x1073, "Disabling FC tape support\n"); + /* Enable Full Login after LIP */ + nv->firmware_options[1] |= BIT_5; + /* Disable FC tapes support */ + nv->add_firmware_options[1] &= ~BIT_4; + } /* Enable initial LIP */ - nv->firmware_options[1] &= BIT_1; - /* Enable FC tapes support */ - nv->add_firmware_options[1] |= BIT_4; - /* Enable Command Queuing in Target Mode */ - nv->add_firmware_options[1] |= BIT_6; + nv->firmware_options[1] &= ~BIT_1; } else { if (ha->saved_set) { - nv->firmware_options[0] = ha->saved_firmware_options[0]; - nv->firmware_options[1] = ha->saved_firmware_options[1]; - nv->add_firmware_options[0] = ha->saved_add_firmware_options[0]; - nv->add_firmware_options[1] = ha->saved_add_firmware_options[1]; + nv->firmware_options[0] = + ha->saved_firmware_options[0]; + nv->firmware_options[1] = + ha->saved_firmware_options[1]; + nv->add_firmware_options[0] = + ha->saved_add_firmware_options[0]; + nv->add_firmware_options[1] = + ha->saved_add_firmware_options[1]; } } } + #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ if (!IS_QLA2100(ha)) { if (ha->enable_class_2) { - if (ha->flags.init_done) { - fc_host_supported_classes(ha->host) = + if (vha->flags.init_done) { + fc_host_supported_classes(vha->host) = FC_COS_CLASS2 | FC_COS_CLASS3; } nv->add_firmware_options[1] |= BIT_0; } else { - if (ha->flags.init_done) { - fc_host_supported_classes(ha->host) = + if (vha->flags.init_done) { + fc_host_supported_classes(vha->host) = FC_COS_CLASS3; } nv->add_firmware_options[1] &= BIT_0; @@ -1803,8 +2442,10 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) /* P2P preferred, otherwise loop */ nv->add_firmware_options[1] |= BIT_5 | BIT_4; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET /* out-of-order frames rassembly */ nv->special_options[0] |= BIT_6; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ if (IS_QLA2300(ha)) { if (ha->fb_rev == FPM_2310) { @@ -1813,7 +2454,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) strcpy(ha->model_number, "QLA2300"); } } else { - qla2x00_set_model_info(ha, nv->model_number, + qla2x00_set_model_info(vha, nv->model_number, sizeof(nv->model_number), "QLA23xx"); } } else if (IS_QLA2200(ha)) { @@ -1851,13 +2492,16 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) /* Prepare nodename */ #ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (ha->node_name_set) { - memcpy(icb->node_name, ha->tgt_node_name, WWN_SIZE); + if (vha->node_name_set || vha->port_name_set) { + if (vha->node_name_set) + memcpy(icb->node_name, vha->node_name, WWN_SIZE); + if (vha->port_name_set) + memcpy(icb->port_name, vha->port_name, WWN_SIZE); icb->firmware_options[1] |= BIT_6; } else -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + /* Use alternate WWN? */ if (nv->host_p[1] & BIT_7) { - /* Use alternate WWN? */ memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); } @@ -1874,8 +2518,13 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) /* * Set host adapter parameters. */ + + /* + * BIT_7 in the host-parameters section allows for modification to + * internal driver logging. + */ if (nv->host_p[0] & BIT_7) - ql2xextended_error_logging = 1; + ql2xextended_error_logging = 0x7fffffff; ha->flags.disable_risc_code_load = ((nv->host_p[0] & BIT_4) ? 1 : 0); /* Always load RISC code on non ISP2[12]00 chips. */ if (!IS_QLA2100(ha) && !IS_QLA2200(ha)) @@ -1896,15 +2545,15 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) ha->serial0 = icb->port_name[5]; ha->serial1 = icb->port_name[6]; ha->serial2 = icb->port_name[7]; - ha->node_name = icb->node_name; - ha->port_name = icb->port_name; + memcpy(vha->node_name, icb->node_name, WWN_SIZE); + memcpy(vha->port_name, icb->port_name, WWN_SIZE); icb->execution_throttle = __constant_cpu_to_le16(0xFFFF); ha->retry_count = nv->retry_count; /* Set minimum login_timeout to 4 seconds. */ - if (nv->login_timeout < ql2xlogintimeout) + if (nv->login_timeout != ql2xlogintimeout) nv->login_timeout = ql2xlogintimeout; if (nv->login_timeout < 4) nv->login_timeout = 4; @@ -1965,10 +2614,10 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) icb->response_accumulation_timer = 3; icb->interrupt_delay_timer = 5; - ha->flags.process_response_queue = 1; + vha->flags.process_response_queue = 1; } else { /* Enable ZIO. */ - if (!ha->flags.init_done) { + if (!vha->flags.init_done) { ha->zio_mode = icb->add_firmware_options[0] & (BIT_3 | BIT_2 | BIT_1 | BIT_0); ha->zio_timer = icb->interrupt_delay_timer ? @@ -1976,26 +2625,23 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) } icb->add_firmware_options[0] &= ~(BIT_3 | BIT_2 | BIT_1 | BIT_0); - ha->flags.process_response_queue = 0; + vha->flags.process_response_queue = 0; if (ha->zio_mode != QLA_ZIO_DISABLED) { ha->zio_mode = QLA_ZIO_MODE_6; - DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer " - "delay (%d us).\n", ha->host_no, ha->zio_mode, - ha->zio_timer * 100)); - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x0068, "ZIO mode %d enabled; timer delay (%d us).\n", ha->zio_mode, ha->zio_timer * 100); icb->add_firmware_options[0] |= (uint8_t)ha->zio_mode; icb->interrupt_delay_timer = (uint8_t)ha->zio_timer; - ha->flags.process_response_queue = 1; + vha->flags.process_response_queue = 1; } } -out: - if (rval) { - DEBUG2_3(printk(KERN_WARNING - "scsi(%ld): NVRAM configuration failed!\n", ha->host_no)); + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x0069, + "NVRAM configuration failed.\n"); } return (rval); } @@ -2005,17 +2651,18 @@ qla2x00_rport_del(void *data) { fc_port_t *fcport = data; struct fc_rport *rport; + unsigned long flags; - spin_lock_irq(fcport->ha->host->host_lock); - rport = fcport->drport; + spin_lock_irqsave(fcport->vha->host->host_lock, flags); + rport = fcport->drport ? fcport->drport: fcport->rport; fcport->drport = NULL; - spin_unlock_irq(fcport->ha->host->host_lock); + spin_unlock_irqrestore(fcport->vha->host->host_lock, flags); if (rport) { fc_remote_port_delete(rport); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_fc_port_deleted) - qla_target.tgt_fc_port_deleted(fcport->ha, fcport); -#endif + qla_target.tgt_fc_port_deleted(fcport->vha, fcport); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ } } @@ -2026,8 +2673,8 @@ qla2x00_rport_del(void *data) * * Returns a pointer to the allocated fcport, or NULL, if none available. */ -static fc_port_t * -qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) +fc_port_t * +qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) { fc_port_t *fcport; @@ -2036,13 +2683,12 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) return NULL; /* Setup fcport template structure. */ - fcport->ha = ha; - fcport->vp_idx = ha->vp_idx; + fcport->vha = vha; fcport->port_type = FCT_UNKNOWN; fcport->loop_id = FC_NO_LOOP_ID; - atomic_set(&fcport->state, FCS_UNCONFIGURED); - fcport->flags = FCF_RLC_SUPPORT; + qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED); fcport->supported_classes = FC_COS_UNSPECIFIED; + fcport->scan_state = QLA_FCPORT_SCAN_NONE; return fcport; } @@ -2059,107 +2705,109 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) * 1 = error. * 2 = database was full and device was not configured. */ - int -qla2x00_configure_loop(scsi_qla_host_t *ha) +int +qla2x00_configure_loop(scsi_qla_host_t *vha) { int rval; unsigned long flags, save_flags; - + struct qla_hw_data *ha = vha->hw; rval = QLA_SUCCESS; /* Get Initiator ID */ - if (test_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags)) { - rval = qla2x00_configure_hba(ha); + if (test_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags)) { + rval = qla2x00_configure_hba(vha); if (rval != QLA_SUCCESS) { - DEBUG(printk("scsi(%ld): Unable to configure HBA.\n", - ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2013, + "Unable to configure HBA.\n"); return (rval); } } - save_flags = flags = ha->dpc_flags; - DEBUG(printk("scsi(%ld): Configure loop -- dpc flags =0x%lx\n", - ha->host_no, flags)); + save_flags = flags = vha->dpc_flags; + ql_dbg(ql_dbg_disc, vha, 0x2014, + "Configure loop -- dpc flags = 0x%lx.\n", flags); /* * If we have both an RSCN and PORT UPDATE pending then handle them * both at the same time. */ - clear_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); - clear_bit(RSCN_UPDATE, &ha->dpc_flags); + clear_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + clear_bit(RSCN_UPDATE, &vha->dpc_flags); + + qla2x00_get_data_rate(vha); /* Determine what we need to do */ if (ha->current_topology == ISP_CFG_FL && (test_bit(LOCAL_LOOP_UPDATE, &flags))) { - ha->flags.rscn_queue_overflow = 1; set_bit(RSCN_UPDATE, &flags); } else if (ha->current_topology == ISP_CFG_F && (test_bit(LOCAL_LOOP_UPDATE, &flags))) { - ha->flags.rscn_queue_overflow = 1; set_bit(RSCN_UPDATE, &flags); clear_bit(LOCAL_LOOP_UPDATE, &flags); } else if (ha->current_topology == ISP_CFG_N) { clear_bit(RSCN_UPDATE, &flags); - } else if (!ha->flags.online || + } else if (!vha->flags.online || (test_bit(ABORT_ISP_ACTIVE, &flags))) { - ha->flags.rscn_queue_overflow = 1; set_bit(RSCN_UPDATE, &flags); set_bit(LOCAL_LOOP_UPDATE, &flags); } if (test_bit(LOCAL_LOOP_UPDATE, &flags)) { - if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) { + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { + ql_dbg(ql_dbg_disc, vha, 0x2015, + "Loop resync needed, failing.\n"); rval = QLA_FUNCTION_FAILED; - } else { - rval = qla2x00_configure_local_loop(ha); - } + } else + rval = qla2x00_configure_local_loop(vha); } if (rval == QLA_SUCCESS && test_bit(RSCN_UPDATE, &flags)) { - if (LOOP_TRANSITION(ha)) { + if (LOOP_TRANSITION(vha)) { + ql_dbg(ql_dbg_disc, vha, 0x201e, + "Needs RSCN update and loop transition.\n"); rval = QLA_FUNCTION_FAILED; - } else { - rval = qla2x00_configure_fabric(ha); } + else + rval = qla2x00_configure_fabric(vha); } if (rval == QLA_SUCCESS) { - if (atomic_read(&ha->loop_down_timer) || - test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) { + if (atomic_read(&vha->loop_down_timer) || + test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { rval = QLA_FUNCTION_FAILED; } else { - atomic_set(&ha->loop_state, LOOP_READY); - - DEBUG(printk("scsi(%ld): LOOP READY\n", ha->host_no)); + atomic_set(&vha->loop_state, LOOP_READY); + ql_dbg(ql_dbg_disc, vha, 0x2069, + "LOOP READY.\n"); } } if (rval) { - DEBUG2_3(printk("%s(%ld): *** FAILED ***\n", - __func__, ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x206a, + "%s *** FAILED ***.\n", __func__); } else { - DEBUG3(printk("%s: exiting normally\n", __func__)); + ql_dbg(ql_dbg_disc, vha, 0x206b, + "%s: exiting normally.\n", __func__); } /* Restore state if a resync event occurred during processing */ - if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) { + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { if (test_bit(LOCAL_LOOP_UPDATE, &save_flags)) - set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); - if (test_bit(RSCN_UPDATE, &save_flags)) - set_bit(RSCN_UPDATE, &ha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + if (test_bit(RSCN_UPDATE, &save_flags)) { + set_bit(RSCN_UPDATE, &vha->dpc_flags); + } } return (rval); } - - /* * qla2x00_configure_local_loop * Updates Fibre Channel Device Database with local loop devices. @@ -2170,8 +2818,8 @@ qla2x00_configure_loop(scsi_qla_host_t *ha) * Returns: * 0 = success. */ - int -qla2x00_configure_local_loop(scsi_qla_host_t *ha) +int +qla2x00_configure_local_loop(scsi_qla_host_t *vha) { int rval, rval2; int found_devs; @@ -2183,30 +2831,40 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) char *id_iter; uint16_t loop_id; uint8_t domain, area, al_pa; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + + if (!qla_ini_mode_enabled(vha)) { + rval = QLA_SUCCESS; + goto out; + } found_devs = 0; new_fcport = NULL; - entries = MAX_FIBRE_DEVICES; + entries = MAX_FIBRE_DEVICES_LOOP; - DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", ha->host_no)); - DEBUG3(qla2x00_get_fcal_position_map(ha, NULL)); + ql_dbg(ql_dbg_disc, vha, 0x2016, + "Getting FCAL position map.\n"); + if (ql2xextended_error_logging & ql_dbg_disc) + qla2x00_get_fcal_position_map(vha, NULL); /* Get list of logged in devices. */ - memset(ha->gid_list, 0, GID_LIST_SIZE); - rval = qla2x00_get_id_list(ha, ha->gid_list, ha->gid_list_dma, + memset(ha->gid_list, 0, qla2x00_gid_list_size(ha)); + rval = qla2x00_get_id_list(vha, ha->gid_list, ha->gid_list_dma, &entries); if (rval != QLA_SUCCESS) goto cleanup_allocation; - DEBUG3(printk("scsi(%ld): Entries in ID list (%d)\n", - ha->host_no, entries)); - DEBUG3(qla2x00_dump_buffer((uint8_t *)ha->gid_list, - entries * sizeof(struct gid_list_info))); + ql_dbg(ql_dbg_disc, vha, 0x2017, + "Entries in ID list (%d).\n", entries); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075, + (uint8_t *)ha->gid_list, + entries * sizeof(struct gid_list_info)); /* Allocate temporary fcport for any new fcports discovered. */ - new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL); + new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (new_fcport == NULL) { + ql_log(ql_log_warn, vha, 0x2018, + "Memory allocation failed for fcport.\n"); rval = QLA_MEMORY_ALLOC_FAILED; goto cleanup_allocation; } @@ -2215,20 +2873,16 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) /* * Mark local devices that were present with FCF_DEVICE_LOST for now. */ - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (fcport->vp_idx != ha->vp_idx) - continue; - + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (atomic_read(&fcport->state) == FCS_ONLINE && fcport->port_type != FCT_BROADCAST && (fcport->flags & FCF_FABRIC_DEVICE) == 0) { - DEBUG(printk("scsi(%ld): Marking port lost, " - "loop_id=0x%04x\n", - ha->host_no, fcport->loop_id)); + ql_dbg(ql_dbg_disc, vha, 0x2019, + "Marking port lost loop_id=0x%04x.\n", + fcport->loop_id); - atomic_set(&fcport->state, FCS_DEVICE_LOST); - fcport->flags &= ~FCF_FARP_DONE; + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); } } @@ -2252,7 +2906,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) /* Bypass if not same domain and area of adapter. */ if (area && domain && - (area != ha->d_id.b.area || domain != ha->d_id.b.domain)) + (area != vha->d_id.b.area || domain != vha->d_id.b.domain)) continue; /* Bypass invalid local loop ID. */ @@ -2264,32 +2918,27 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) new_fcport->d_id.b.area = area; new_fcport->d_id.b.al_pa = al_pa; new_fcport->loop_id = loop_id; - new_fcport->vp_idx = ha->vp_idx; - rval2 = qla2x00_get_port_database(ha, new_fcport, 0); + rval2 = qla2x00_get_port_database(vha, new_fcport, 0); if (rval2 != QLA_SUCCESS) { - DEBUG2(printk("scsi(%ld): Failed to retrieve fcport " - "information -- get_port_database=%x, " - "loop_id=0x%04x\n", - ha->host_no, rval2, new_fcport->loop_id)); - DEBUG2(printk("scsi(%ld): Scheduling resync...\n", - ha->host_no)); - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); + ql_dbg(ql_dbg_disc, vha, 0x201a, + "Failed to retrieve fcport information " + "-- get_port_database=%x, loop_id=0x%04x.\n", + rval2, new_fcport->loop_id); + ql_dbg(ql_dbg_disc, vha, 0x201b, + "Scheduling resync.\n"); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); continue; } /* Check for matching device in port list. */ found = 0; fcport = NULL; - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (fcport->vp_idx != ha->vp_idx) - continue; - + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (memcmp(new_fcport->port_name, fcport->port_name, WWN_SIZE)) continue; - fcport->flags &= ~(FCF_FABRIC_DEVICE | - FCF_PERSISTENT_BOUND); + fcport->flags &= ~FCF_FABRIC_DEVICE; fcport->loop_id = new_fcport->loop_id; fcport->port_type = new_fcport->port_type; fcport->d_id.b24 = new_fcport->d_id.b24; @@ -2302,19 +2951,14 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) if (!found) { /* New device, add to fcports list. */ - new_fcport->flags &= ~FCF_PERSISTENT_BOUND; - if (ha->parent) { - new_fcport->ha = ha; - new_fcport->vp_idx = ha->vp_idx; - list_add_tail(&new_fcport->vp_fcport, - &ha->vp_fcports); - } - list_add_tail_rcu(&new_fcport->list, &pha->fcports); + list_add_tail_rcu(&new_fcport->list, &vha->vp_fcports); /* Allocate a new replacement fcport. */ fcport = new_fcport; - new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL); + new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (new_fcport == NULL) { + ql_log(ql_log_warn, vha, 0x201c, + "Failed to allocate memory for fcport.\n"); rval = QLA_MEMORY_ALLOC_FAILED; goto cleanup_allocation; } @@ -2324,7 +2968,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) /* Base iIDMA settings on HBA port speed. */ fcport->fp_speed = ha->link_data_rate; - qla2x00_update_fcport(ha, fcport); + qla2x00_update_fcport(vha, fcport); found_devs++; } @@ -2333,82 +2977,82 @@ cleanup_allocation: kfree(new_fcport); if (rval != QLA_SUCCESS) { - DEBUG2(printk("scsi(%ld): Configure local loop error exit: " - "rval=%x\n", ha->host_no, rval)); - } - - if (found_devs) { - ha->device_flags |= DFLG_LOCAL_DEVICES; - ha->device_flags &= ~DFLG_RETRY_LOCAL_DEVICES; + ql_dbg(ql_dbg_disc, vha, 0x201d, + "Configure local loop error exit: rval=%x.\n", rval); } +out: return (rval); } static void -qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) +qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) { -#define LS_UNKNOWN 2 - static char *link_speeds[5] = { "1", "2", "?", "4", "8" }; + char *link_speed; int rval; uint16_t mb[6]; + struct qla_hw_data *ha = vha->hw; if (!IS_IIDMA_CAPABLE(ha)) return; + if (atomic_read(&fcport->state) != FCS_ONLINE) + return; + if (fcport->fp_speed == PORT_SPEED_UNKNOWN || fcport->fp_speed > ha->link_data_rate) return; - rval = qla2x00_set_idma_speed(ha, fcport->loop_id, fcport->fp_speed, + rval = qla2x00_set_idma_speed(vha, fcport->loop_id, fcport->fp_speed, mb); if (rval != QLA_SUCCESS) { - DEBUG2(printk("scsi(%ld): Unable to adjust iIDMA " - "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x %04x.\n", - ha->host_no, fcport->port_name[0], fcport->port_name[1], + ql_dbg(ql_dbg_disc, vha, 0x2004, + "Unable to adjust iIDMA " + "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x " + "%04x.\n", fcport->port_name[0], fcport->port_name[1], fcport->port_name[2], fcport->port_name[3], fcport->port_name[4], fcport->port_name[5], fcport->port_name[6], fcport->port_name[7], rval, - fcport->fp_speed, mb[0], mb[1])); + fcport->fp_speed, mb[0], mb[1]); } else { - DEBUG2(qla_printk(KERN_INFO, ha, - "iIDMA adjusted to %s GB/s on " - "%02x%02x%02x%02x%02x%02x%02x%02x.\n", - link_speeds[fcport->fp_speed], fcport->port_name[0], - fcport->port_name[1], fcport->port_name[2], - fcport->port_name[3], fcport->port_name[4], - fcport->port_name[5], fcport->port_name[6], - fcport->port_name[7])); + link_speed = qla2x00_get_link_speed_str(ha); + ql_dbg(ql_dbg_disc, vha, 0x2005, + "iIDMA adjusted to %s GB/s " + "on %02x%02x%02x%02x%02x%02x%02x%02x.\n", link_speed, + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7]); } } static void -qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) +qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) { struct fc_rport_identifiers rport_ids; struct fc_rport *rport; + unsigned long flags; - if (fcport->drport) - qla2x00_rport_del(fcport); + qla2x00_rport_del(fcport); rport_ids.node_name = wwn_to_u64(fcport->node_name); rport_ids.port_name = wwn_to_u64(fcport->port_name); rport_ids.port_id = fcport->d_id.b.domain << 16 | fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; - fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids); + fcport->rport = rport = fc_remote_port_add(vha->host, 0, &rport_ids); if (!rport) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate fc remote port!\n"); + ql_log(ql_log_warn, vha, 0x2006, + "Unable to allocate fc remote port.\n"); return; } #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_fc_port_added) - qla_target.tgt_fc_port_added(ha, fcport); -#endif - spin_lock_irq(fcport->ha->host->host_lock); + qla_target.tgt_fc_port_added(fcport->vha, fcport); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + spin_lock_irqsave(fcport->vha->host->host_lock, flags); *((fc_port_t **)rport->dd_data) = fcport; - spin_unlock_irq(fcport->ha->host->host_lock); + spin_unlock_irqrestore(fcport->vha->host->host_lock, flags); rport->supported_classes = fcport->supported_classes; @@ -2436,23 +3080,16 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) * Kernel context. */ void -qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) +qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) { - scsi_qla_host_t *pha = to_qla_parent(ha); - - fcport->ha = ha; + fcport->vha = vha; fcport->login_retry = 0; - fcport->port_login_retry_count = pha->port_down_retry_count * - PORT_RETRY_TIME; - atomic_set(&fcport->port_down_timer, pha->port_down_retry_count * - PORT_RETRY_TIME); - fcport->flags &= ~FCF_LOGIN_NEEDED; + fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); - qla2x00_iidma_fcport(ha, fcport); - - atomic_set(&fcport->state, FCS_ONLINE); - - qla2x00_reg_remote_port(ha, fcport); + qla2x00_iidma_fcport(vha, fcport); + qla24xx_update_fcport_fcp_prio(vha, fcport); + qla2x00_reg_remote_port(vha, fcport); + qla2x00_set_fcport_state(fcport, FCS_ONLINE); } /* @@ -2466,204 +3103,144 @@ qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) * 0 = success. * BIT_0 = error */ - int -qla2x00_configure_fabric(scsi_qla_host_t *ha) +int +qla2x00_configure_fabric(scsi_qla_host_t *vha) { - int rval, rval2; - fc_port_t *fcport, *fcptemp; + int rval; + fc_port_t *fcport; uint16_t next_loopid; uint16_t mb[MAILBOX_REGISTER_COUNT]; uint16_t loop_id; LIST_HEAD(new_fcports); - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); /* If FL port exists, then SNS is present */ if (IS_FWI2_CAPABLE(ha)) loop_id = NPH_F_PORT; else loop_id = SNS_FL_PORT; - rval = qla2x00_get_port_name(ha, loop_id, ha->fabric_node_name, 1); + rval = qla2x00_get_port_name(vha, loop_id, vha->fabric_node_name, 1); if (rval != QLA_SUCCESS) { - DEBUG2(printk("scsi(%ld): MBC_GET_PORT_NAME Failed, No FL " - "Port\n", ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x201f, + "MBX_GET_PORT_NAME failed, No FL Port.\n"); - ha->device_flags &= ~SWITCH_FOUND; + vha->device_flags &= ~SWITCH_FOUND; return (QLA_SUCCESS); } - ha->device_flags |= SWITCH_FOUND; + vha->device_flags |= SWITCH_FOUND; - /* Mark devices that need re-synchronization. */ - rval2 = qla2x00_device_resync(ha); - if (rval2 == QLA_RSCNS_HANDLED) { - /* No point doing the scan, just continue. */ - return (QLA_SUCCESS); - } do { /* FDMI support. */ if (ql2xfdmienable && - test_and_clear_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags)) - qla2x00_fdmi_register(ha); + test_and_clear_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags)) + qla2x00_fdmi_register(vha); /* Ensure we are logged into the SNS. */ if (IS_FWI2_CAPABLE(ha)) loop_id = NPH_SNS; else loop_id = SIMPLE_NAME_SERVER; - ha->isp_ops->fabric_login(ha, loop_id, 0xff, 0xff, - 0xfc, mb, BIT_1 | BIT_0); + rval = ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff, + 0xfc, mb, BIT_1|BIT_0); + if (rval != QLA_SUCCESS) { + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + break; + } if (mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x " - "mb[2]=%x mb[6]=%x mb[7]=%x\n", loop_id, - mb[0], mb[1], mb[2], mb[6], mb[7])); + ql_dbg(ql_dbg_disc, vha, 0x2042, + "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x " + "mb[6]=%x mb[7]=%x.\n", loop_id, mb[0], mb[1], + mb[2], mb[6], mb[7]); return (QLA_SUCCESS); } - if (test_and_clear_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags)) { - if (qla2x00_rft_id(ha)) { + if (test_and_clear_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags)) { + if (qla2x00_rft_id(vha)) { /* EMPTY */ - DEBUG2(printk("scsi(%ld): Register FC-4 " - "TYPE failed.\n", ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2045, + "Register FC-4 TYPE failed.\n"); } - if (qla2x00_rff_id(ha)) { + if (qla2x00_rff_id(vha)) { /* EMPTY */ - DEBUG2(printk("scsi(%ld): Register FC-4 " - "Features failed.\n", ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2049, + "Register FC-4 Features failed.\n"); } - if (qla2x00_rnn_id(ha)) { + if (qla2x00_rnn_id(vha)) { /* EMPTY */ - DEBUG2(printk("scsi(%ld): Register Node Name " - "failed.\n", ha->host_no)); - } else if (qla2x00_rsnn_nn(ha)) { + ql_dbg(ql_dbg_disc, vha, 0x204f, + "Register Node Name failed.\n"); + } else if (qla2x00_rsnn_nn(vha)) { /* EMPTY */ - DEBUG2(printk("scsi(%ld): Register Symbolic " - "Node Name failed.\n", ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2053, + "Register Symobilic Node Name failed.\n"); } } - rval = qla2x00_find_all_fabric_devs(ha, &new_fcports); - if (rval != QLA_SUCCESS) { + rval = qla2x00_find_all_fabric_devs(vha, &new_fcports); + if (rval != QLA_SUCCESS) break; - } - /* - * Logout all previous fabric devices marked lost, except - * tape devices. - */ - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (fcport->vp_idx !=ha->vp_idx) - continue; + /* Add new ports to existing port list */ + list_splice_tail_init(&new_fcports, &vha->vp_fcports); - if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) + /* Starting free loop ID. */ + next_loopid = ha->min_external_loopid; + + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) break; if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) continue; - if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) { - qla2x00_mark_device_lost(ha, fcport, + /* Logout lost/gone fabric devices (non-FCP2) */ + if (fcport->scan_state != QLA_FCPORT_SCAN_FOUND && + atomic_read(&fcport->state) == FCS_ONLINE) { + qla2x00_mark_device_lost(vha, fcport, ql2xplogiabsentdevice, 0); if (fcport->loop_id != FC_NO_LOOP_ID && - (fcport->flags & FCF_TAPE_PRESENT) == 0 && + (fcport->flags & FCF_FCP2_DEVICE) == 0 && fcport->port_type != FCT_INITIATOR && fcport->port_type != FCT_BROADCAST) { - ha->isp_ops->fabric_logout(ha, + ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); - fcport->loop_id = FC_NO_LOOP_ID; + } + continue; + } + fcport->scan_state = QLA_FCPORT_SCAN_NONE; + + /* Login fabric devices that need a login */ + if ((fcport->flags & FCF_LOGIN_NEEDED) != 0 && + atomic_read(&vha->loop_down_timer) == 0) { + if (fcport->loop_id == FC_NO_LOOP_ID) { + fcport->loop_id = next_loopid; + rval = qla2x00_find_new_loop_id( + base_vha, fcport); + if (rval != QLA_SUCCESS) { + /* Ran out of IDs to use */ + continue; + } } } - } - /* Starting free loop ID. */ - next_loopid = pha->min_external_loopid; - - /* - * Scan through our port list and login entries that need to be - * logged in. - */ - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (fcport->vp_idx != ha->vp_idx) - continue; - - if (atomic_read(&ha->loop_down_timer) || - test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) - break; - - if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 || - (fcport->flags & FCF_LOGIN_NEEDED) == 0) - continue; - - if (fcport->loop_id == FC_NO_LOOP_ID) { - fcport->loop_id = next_loopid; - rval = qla2x00_find_new_loop_id( - to_qla_parent(ha), fcport); - if (rval != QLA_SUCCESS) { - /* Ran out of IDs to use */ - break; - } - } - /* Login and update database */ - qla2x00_fabric_dev_login(ha, fcport, &next_loopid); - } - - /* Exit if out of loop IDs. */ - if (rval != QLA_SUCCESS) { - break; - } - - /* - * Login and add the new devices to our port list. - */ - list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) { - if (atomic_read(&ha->loop_down_timer) || - test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) - break; - - /* Find a new loop ID to use. */ - fcport->loop_id = next_loopid; - rval = qla2x00_find_new_loop_id(to_qla_parent(ha), - fcport); - if (rval != QLA_SUCCESS) { - /* Ran out of IDs to use */ - break; - } - - /* Login and update database */ - qla2x00_fabric_dev_login(ha, fcport, &next_loopid); - - if (ha->parent) { - fcport->ha = ha; - fcport->vp_idx = ha->vp_idx; - list_add_tail(&fcport->vp_fcport, - &ha->vp_fcports); - list_del(&fcport->list); - list_add_tail_rcu(&fcport->list, - &ha->parent->fcports); - } else { - list_del(&fcport->list); - list_add_tail_rcu(&fcport->list, &ha->fcports); - } + /* Login and update database, if needed */ + if (qla_ini_mode_enabled(vha)) + qla2x00_fabric_dev_login(vha, fcport, &next_loopid); } } while (0); - /* Free all new device structures not processed. */ - list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) { - list_del(&fcport->list); - kfree(fcport); - } - if (rval) { - DEBUG2(printk("scsi(%ld): Configure fabric error exit: " - "rval=%d\n", ha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x2068, + "Configure fabric error exit rval=%d.\n", rval); } return (rval); } - /* * qla2x00_find_all_fabric_devs * @@ -2678,7 +3255,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) * Kernel context. */ static int -qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) +qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, + struct list_head *new_fcports) { int rval; uint16_t loop_id; @@ -2688,58 +3266,72 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) sw_info_t *swl; int swl_idx; int first_dev, last_dev; - port_id_t wrap, nxt_d_id; - int vp_index; - int empty_vp_index; - int found_vp; - scsi_qla_host_t *vha; - scsi_qla_host_t *pha = to_qla_parent(ha); + port_id_t wrap = {}, nxt_d_id; + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *vp, *base_vha = pci_get_drvdata(ha->pdev); + struct scsi_qla_host *tvp; rval = QLA_SUCCESS; + + if (!qla_ini_mode_enabled(vha)) + goto out; + wrap.b24 = 0; /* undefined */ /* Try GID_PT to get device list, else GAN. */ - swl = kcalloc(MAX_FIBRE_DEVICES, sizeof(sw_info_t), GFP_ATOMIC); + if (!ha->swl) + ha->swl = kcalloc(ha->max_fibre_devices, sizeof(sw_info_t), + GFP_KERNEL); + swl = ha->swl; if (!swl) { /*EMPTY*/ - DEBUG2(printk("scsi(%ld): GID_PT allocations failed, fallback " - "on GA_NXT\n", ha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2054, + "GID_PT allocations failed, fallback on GA_NXT.\n"); } else { - if (qla2x00_gid_pt(ha, swl) != QLA_SUCCESS) { - kfree(swl); + memset(swl, 0, ha->max_fibre_devices * sizeof(sw_info_t)); + if (qla2x00_gid_pt(vha, swl) != QLA_SUCCESS) { swl = NULL; - } else if (qla2x00_gpn_id(ha, swl) != QLA_SUCCESS) { - kfree(swl); + } else if (qla2x00_gpn_id(vha, swl) != QLA_SUCCESS) { swl = NULL; - } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) { - kfree(swl); + } else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) { swl = NULL; - } else if (qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) { - qla2x00_gpsc(ha, swl); + } else if (ql2xiidmaenable && + qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) { + qla2x00_gpsc(vha, swl); } + + /* If other queries succeeded probe for FC-4 type */ + if (swl) + qla2x00_gff_id(vha, swl); } swl_idx = 0; /* Allocate temporary fcport for any new fcports discovered. */ - new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL); + new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (new_fcport == NULL) { - kfree(swl); + ql_log(ql_log_warn, vha, 0x205e, + "Failed to allocate memory for fcport.\n"); return (QLA_MEMORY_ALLOC_FAILED); } new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED); - new_fcport->vp_idx = ha->vp_idx; /* Set start port ID scan at adapter ID. */ first_dev = 1; last_dev = 0; /* Starting free loop ID. */ - loop_id = pha->min_external_loopid; - for (; loop_id <= ha->last_loop_id; loop_id++) { - if (qla2x00_is_reserved_id(ha, loop_id)) + loop_id = ha->min_external_loopid; + for (; loop_id <= ha->max_loop_id; loop_id++) { + if (qla2x00_is_reserved_id(vha, loop_id)) continue; - if (atomic_read(&ha->loop_down_timer) || LOOP_TRANSITION(ha)) + if (ha->current_topology == ISP_CFG_FL && + (atomic_read(&vha->loop_down_timer) || + LOOP_TRANSITION(vha))) { + atomic_set(&vha->loop_down_timer, 0); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); break; + } if (swl != NULL) { if (last_dev) { @@ -2753,6 +3345,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) memcpy(new_fcport->fabric_port_name, swl[swl_idx].fabric_port_name, WWN_SIZE); new_fcport->fp_speed = swl[swl_idx].fp_speed; + new_fcport->fc4_type = swl[swl_idx].fc4_type; if (swl[swl_idx].d_id.b.rsvd_1 != 0) { last_dev = 1; @@ -2761,11 +3354,11 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) } } else { /* Send GA_NXT to the switch */ - rval = qla2x00_ga_nxt(ha, new_fcport); + rval = qla2x00_ga_nxt(vha, new_fcport); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "SNS scan failed -- assuming zero-entry " - "result...\n"); + ql_log(ql_log_warn, vha, 0x2064, + "SNS scan failed -- assuming " + "zero-entry result.\n"); list_for_each_entry_safe(fcport, fcptemp, new_fcports, list) { list_del(&fcport->list); @@ -2781,45 +3374,39 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) wrap.b24 = new_fcport->d_id.b24; first_dev = 0; } else if (new_fcport->d_id.b24 == wrap.b24) { - DEBUG2(printk("scsi(%ld): device wrap (%02x%02x%02x)\n", - ha->host_no, new_fcport->d_id.b.domain, - new_fcport->d_id.b.area, new_fcport->d_id.b.al_pa)); + ql_dbg(ql_dbg_disc, vha, 0x2065, + "Device wrap (%02x%02x%02x).\n", + new_fcport->d_id.b.domain, + new_fcport->d_id.b.area, + new_fcport->d_id.b.al_pa); break; } /* Bypass if same physical adapter. */ - if (new_fcport->d_id.b24 == pha->d_id.b24) + if (new_fcport->d_id.b24 == base_vha->d_id.b24) continue; /* Bypass virtual ports of the same host. */ - if (pha->num_vhosts) { - for_each_mapped_vp_idx(pha, vp_index) { - empty_vp_index = 1; - found_vp = 0; - list_for_each_entry(vha, &pha->vp_list, - vp_list) { - if (vp_index == vha->vp_idx) { - empty_vp_index = 0; - found_vp = 1; - break; - } - } + found = 0; + if (ha->num_vhosts) { + unsigned long flags; - if (empty_vp_index) - continue; - - if (found_vp && - new_fcport->d_id.b24 == vha->d_id.b24) + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) { + if (new_fcport->d_id.b24 == vp->d_id.b24) { + found = 1; break; + } } + spin_unlock_irqrestore(&ha->vport_slock, flags); - if (vp_index <= pha->max_npiv_vports) + if (found) continue; } /* Bypass if same domain and area of adapter. */ if (((new_fcport->d_id.b24 & 0xffff00) == - (ha->d_id.b24 & 0xffff00)) && ha->current_topology == + (vha->d_id.b24 & 0xffff00)) && ha->current_topology == ISP_CFG_FL) continue; @@ -2827,15 +3414,21 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) if ((new_fcport->d_id.b.domain & 0xf0) == 0xf0) continue; + /* Bypass ports whose FCP-4 type is not FCP_SCSI */ + if (ql2xgffidenable && + (new_fcport->fc4_type != FC4_TYPE_FCP_SCSI && + new_fcport->fc4_type != FC4_TYPE_UNKNOWN)) + continue; + /* Locate matching device in database. */ found = 0; - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (new_fcport->vp_idx != fcport->vp_idx) - continue; + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (memcmp(new_fcport->port_name, fcport->port_name, WWN_SIZE)) continue; + fcport->scan_state = QLA_FCPORT_SCAN_FOUND; + found++; /* Update port state. */ @@ -2860,7 +3453,6 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) fcport->loop_id = FC_NO_LOOP_ID; fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED); - fcport->flags &= ~FCF_PERSISTENT_BOUND; break; } @@ -2872,10 +3464,11 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) fcport->d_id.b24 = new_fcport->d_id.b24; fcport->flags |= FCF_LOGIN_NEEDED; if (fcport->loop_id != FC_NO_LOOP_ID && - (fcport->flags & FCF_TAPE_PRESENT) == 0 && + (fcport->flags & FCF_FCP2_DEVICE) == 0 && + (fcport->flags & FCF_ASYNC_SENT) == 0 && fcport->port_type != FCT_INITIATOR && fcport->port_type != FCT_BROADCAST) { - ha->isp_ops->fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); fcport->loop_id = FC_NO_LOOP_ID; @@ -2886,28 +3479,24 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) if (found) continue; - /* If device was not in our fcports list, then add it. */ - list_add_tail_rcu(&new_fcport->list, new_fcports); + list_add_tail(&new_fcport->list, new_fcports); /* Allocate a new replacement fcport. */ nxt_d_id.b24 = new_fcport->d_id.b24; - new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL); + new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (new_fcport == NULL) { - kfree(swl); + ql_log(ql_log_warn, vha, 0x2066, + "Memory allocation failed for fcport.\n"); return (QLA_MEMORY_ALLOC_FAILED); } new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED); new_fcport->d_id.b24 = nxt_d_id.b24; - new_fcport->vp_idx = ha->vp_idx; } - kfree(swl); kfree(new_fcport); - if (!list_empty(new_fcports)) - ha->device_flags |= DFLG_FABRIC_DEVICES; - +out: return (rval); } @@ -2925,14 +3514,17 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) * Context: * Kernel context. */ -static int -qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev) +int +qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev) { int rval; int found; fc_port_t *fcport; uint16_t first_loop_id; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *vp; + struct scsi_qla_host *tvp; + unsigned long flags = 0; rval = QLA_SUCCESS; @@ -2941,17 +3533,15 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev) for (;;) { /* Skip loop ID if already used by adapter. */ - if (dev->loop_id == ha->loop_id) { + if (dev->loop_id == vha->loop_id) dev->loop_id++; - } /* Skip reserved loop IDs. */ - while (qla2x00_is_reserved_id(ha, dev->loop_id)) { + while (qla2x00_is_reserved_id(vha, dev->loop_id)) dev->loop_id++; - } /* Reset loop ID if passed the end. */ - if (dev->loop_id > ha->last_loop_id) { + if (dev->loop_id > ha->max_loop_id) { /* first loop ID. */ dev->loop_id = ha->min_external_loopid; } @@ -2959,16 +3549,27 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev) /* Check for loop ID being already in use. */ found = 0; fcport = NULL; - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (fcport->loop_id == dev->loop_id && fcport != dev) { - /* ID possibly in use */ - found++; - break; + + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) { + list_for_each_entry_rcu(fcport, &vp->vp_fcports, list) { + if (fcport->loop_id == dev->loop_id && + fcport != dev) { + /* ID possibly in use */ + found++; + break; + } } + if (found) + break; } + spin_unlock_irqrestore(&ha->vport_slock, flags); /* If not in use then it is free to use. */ if (!found) { + ql_dbg(ql_dbg_disc, dev->vha, 0x2086, + "Assigning new loopid=%x, portid=%x.\n", + dev->loop_id, dev->d_id.b24); break; } @@ -2986,116 +3587,6 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev) return (rval); } -/* - * qla2x00_device_resync - * Marks devices in the database that needs resynchronization. - * - * Input: - * ha = adapter block pointer. - * - * Context: - * Kernel context. - */ -static int -qla2x00_device_resync(scsi_qla_host_t *ha) -{ - int rval; - uint32_t mask; - fc_port_t *fcport; - uint32_t rscn_entry; - uint8_t rscn_out_iter; - uint8_t format; - port_id_t d_id; - scsi_qla_host_t *pha = to_qla_parent(ha); - - rval = QLA_RSCNS_HANDLED; - - while (ha->rscn_out_ptr != ha->rscn_in_ptr || - ha->flags.rscn_queue_overflow) { - - rscn_entry = ha->rscn_queue[ha->rscn_out_ptr]; - format = MSB(MSW(rscn_entry)); - d_id.b.domain = LSB(MSW(rscn_entry)); - d_id.b.area = MSB(LSW(rscn_entry)); - d_id.b.al_pa = LSB(LSW(rscn_entry)); - - DEBUG(printk("scsi(%ld): RSCN queue entry[%d] = " - "[%02x/%02x%02x%02x].\n", - ha->host_no, ha->rscn_out_ptr, format, d_id.b.domain, - d_id.b.area, d_id.b.al_pa)); - - ha->rscn_out_ptr++; - if (ha->rscn_out_ptr == MAX_RSCN_COUNT) - ha->rscn_out_ptr = 0; - - /* Skip duplicate entries. */ - for (rscn_out_iter = ha->rscn_out_ptr; - !ha->flags.rscn_queue_overflow && - rscn_out_iter != ha->rscn_in_ptr; - rscn_out_iter = (rscn_out_iter == - (MAX_RSCN_COUNT - 1)) ? 0: rscn_out_iter + 1) { - - if (rscn_entry != ha->rscn_queue[rscn_out_iter]) - break; - - DEBUG(printk("scsi(%ld): Skipping duplicate RSCN queue " - "entry found at [%d].\n", ha->host_no, - rscn_out_iter)); - - ha->rscn_out_ptr = rscn_out_iter; - } - - /* Queue overflow, set switch default case. */ - if (ha->flags.rscn_queue_overflow) { - DEBUG(printk("scsi(%ld): device_resync: rscn " - "overflow.\n", ha->host_no)); - - format = 3; - ha->flags.rscn_queue_overflow = 0; - } - - switch (format) { - case 0: - mask = 0xffffff; - break; - case 1: - mask = 0xffff00; - break; - case 2: - mask = 0xff0000; - break; - default: - mask = 0x0; - d_id.b24 = 0; - ha->rscn_out_ptr = ha->rscn_in_ptr; - break; - } - - rval = QLA_SUCCESS; - - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (fcport->vp_idx != ha->vp_idx) - continue; - - if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 || - (fcport->d_id.b24 & mask) != d_id.b24 || - fcport->port_type == FCT_BROADCAST) - continue; - - if (atomic_read(&fcport->state) == FCS_ONLINE) { -#ifndef CONFIG_SCSI_QLA2XXX_TARGET - if (format != 3 || - fcport->port_type != FCT_INITIATOR) -#endif - qla2x00_mark_device_lost(ha, fcport, - 0, 0); - } - fcport->flags &= ~FCF_FARP_DONE; - } - } - return (rval); -} - /* * qla2x00_fabric_dev_login * Login fabric target device and update FC port database. @@ -3113,31 +3604,45 @@ qla2x00_device_resync(scsi_qla_host_t *ha) * Kernel context. */ static int -qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport, +qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t *next_loopid) { int rval; int retry; uint8_t opts; + struct qla_hw_data *ha = vha->hw; rval = QLA_SUCCESS; retry = 0; - rval = qla2x00_fabric_login(ha, fcport, next_loopid); + if (IS_ALOGIO_CAPABLE(ha)) { + if (fcport->flags & FCF_ASYNC_SENT) + return rval; + fcport->flags |= FCF_ASYNC_SENT; + rval = qla2x00_post_async_login_work(vha, fcport, NULL); + if (!rval) + return rval; + } + + fcport->flags &= ~FCF_ASYNC_SENT; + rval = qla2x00_fabric_login(vha, fcport, next_loopid); if (rval == QLA_SUCCESS) { - /* Send an ADISC to tape devices.*/ + /* Send an ADISC to FCP2 devices.*/ opts = 0; - if (fcport->flags & FCF_TAPE_PRESENT) + if (fcport->flags & FCF_FCP2_DEVICE) opts |= BIT_1; - rval = qla2x00_get_port_database(ha, fcport, opts); + rval = qla2x00_get_port_database(vha, fcport, opts); if (rval != QLA_SUCCESS) { - ha->isp_ops->fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); - qla2x00_mark_device_lost(ha, fcport, 1, 0); + qla2x00_mark_device_lost(vha, fcport, 1, 0); } else { - qla2x00_update_fcport(ha, fcport); + qla2x00_update_fcport(vha, fcport); } + } else { + /* Retry Login. */ + qla2x00_mark_device_lost(vha, fcport, 1, 0); } return (rval); @@ -3158,27 +3663,32 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport, * 3 - Fatal error */ int -qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, +qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t *next_loopid) { int rval; int retry; uint16_t tmp_loopid; uint16_t mb[MAILBOX_REGISTER_COUNT]; + struct qla_hw_data *ha = vha->hw; retry = 0; tmp_loopid = 0; for (;;) { - DEBUG(printk("scsi(%ld): Trying Fabric Login w/loop id 0x%04x " - "for port %02x%02x%02x.\n", - ha->host_no, fcport->loop_id, fcport->d_id.b.domain, - fcport->d_id.b.area, fcport->d_id.b.al_pa)); + ql_dbg(ql_dbg_disc, vha, 0x2000, + "Trying Fabric Login w/loop id 0x%04x for port " + "%02x%02x%02x.\n", + fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); /* Login fcport on switch. */ - ha->isp_ops->fabric_login(ha, fcport->loop_id, + rval = ha->isp_ops->fabric_login(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, mb, BIT_0); + if (rval != QLA_SUCCESS) { + return rval; + } if (mb[0] == MBS_PORT_ID_USED) { /* * Device has another loop ID. The firmware team @@ -3191,10 +3701,11 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, tmp_loopid = fcport->loop_id; fcport->loop_id = mb[1]; - DEBUG(printk("Fabric Login: port in use - next " - "loop id=0x%04x, port Id=%02x%02x%02x.\n", + ql_dbg(ql_dbg_disc, vha, 0x2001, + "Fabric Login: port in use - next loop " + "id=0x%04x, port id= %02x%02x%02x.\n", fcport->loop_id, fcport->d_id.b.domain, - fcport->d_id.b.area, fcport->d_id.b.al_pa)); + fcport->d_id.b.area, fcport->d_id.b.al_pa); } else if (mb[0] == MBS_COMMAND_COMPLETE) { /* @@ -3216,7 +3727,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, } else { fcport->port_type = FCT_TARGET; if (mb[1] & BIT_1) { - fcport->flags |= FCF_TAPE_PRESENT; + fcport->flags |= FCF_FCP2_DEVICE; } } @@ -3225,12 +3736,14 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, if (mb[10] & BIT_1) fcport->supported_classes |= FC_COS_CLASS3; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET if (IS_FWI2_CAPABLE(ha)) { if (mb[10] & BIT_7) fcport->conf_compl_supported = 1; } else { /* mb[10] bits are undocumented, ToDo */ } +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ rval = QLA_SUCCESS; break; @@ -3239,7 +3752,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, * Loop ID already used, try next loop ID. */ fcport->loop_id++; - rval = qla2x00_find_new_loop_id(ha, fcport); + rval = qla2x00_find_new_loop_id(vha, fcport); if (rval != QLA_SUCCESS) { /* Ran out of loop IDs to use */ break; @@ -3251,10 +3764,10 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, * dead. */ *next_loopid = fcport->loop_id; - ha->isp_ops->fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); - qla2x00_mark_device_lost(ha, fcport, 1, 0); + qla2x00_mark_device_lost(vha, fcport, 1, 0); rval = 1; break; @@ -3262,14 +3775,14 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, /* * unrecoverable / not handled error */ - DEBUG2(printk("%s(%ld): failed=%x port_id=%02x%02x%02x " - "loop_id=%x jiffies=%lx.\n", - __func__, ha->host_no, mb[0], - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa, fcport->loop_id, jiffies)); + ql_dbg(ql_dbg_disc, vha, 0x2002, + "Failed=%x port_id=%02x%02x%02x loop_id=%x " + "jiffies=%lx.\n", mb[0], fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + fcport->loop_id, jiffies); *next_loopid = fcport->loop_id; - ha->isp_ops->fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); fcport->loop_id = FC_NO_LOOP_ID; @@ -3297,13 +3810,13 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, * 3 - Fatal error */ int -qla2x00_local_device_login(scsi_qla_host_t *ha, fc_port_t *fcport) +qla2x00_local_device_login(scsi_qla_host_t *vha, fc_port_t *fcport) { int rval; uint16_t mb[MAILBOX_REGISTER_COUNT]; memset(mb, 0, sizeof(mb)); - rval = qla2x00_login_local_device(ha, fcport, mb, BIT_0); + rval = qla2x00_login_local_device(vha, fcport, mb, BIT_0); if (rval == QLA_SUCCESS) { /* Interrogate mailbox registers for any errors */ if (mb[0] == MBS_COMMAND_ERROR) @@ -3327,57 +3840,222 @@ qla2x00_local_device_login(scsi_qla_host_t *ha, fc_port_t *fcport) * 0 = success */ int -qla2x00_loop_resync(scsi_qla_host_t *ha) +qla2x00_loop_resync(scsi_qla_host_t *vha) { - int rval; + int rval = QLA_SUCCESS; uint32_t wait_time; + struct req_que *req; + struct rsp_que *rsp; - rval = QLA_SUCCESS; + if (vha->hw->flags.cpu_affinity_enabled) + req = vha->hw->req_q_map[0]; + else + req = vha->req; + rsp = req->rsp; - atomic_set(&ha->loop_state, LOOP_UPDATE); - clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags); - if (ha->flags.online) { - if (!(rval = qla2x00_fw_ready(ha))) { + clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + if (vha->flags.online) { + if (!(rval = qla2x00_fw_ready(vha))) { /* Wait at most MAX_TARGET RSCNs for a stable link. */ wait_time = 256; do { - atomic_set(&ha->loop_state, LOOP_UPDATE); - /* Issue a marker after FW becomes ready. */ - qla2x00_marker(ha, 0, 0, MK_SYNC_ALL); - ha->marker_needed = 0; + qla2x00_marker(vha, req, rsp, 0, 0, + MK_SYNC_ALL); + vha->marker_needed = 0; /* Remap devices on Loop. */ - clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); + clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); - qla2x00_configure_loop(ha); + qla2x00_configure_loop(vha); wait_time--; - } while (!atomic_read(&ha->loop_down_timer) && - !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) && - wait_time && - (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))); + } while (!atomic_read(&vha->loop_down_timer) && + !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) + && wait_time && (test_bit(LOOP_RESYNC_NEEDED, + &vha->dpc_flags))); } } - if (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) { + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) return (QLA_FUNCTION_FAILED); - } - if (rval != QLA_SUCCESS) - DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__)); + if (rval) + ql_dbg(ql_dbg_disc, vha, 0x206c, + "%s *** FAILED ***.\n", __func__); return (rval); } +/* +* qla2x00_perform_loop_resync +* Description: This function will set the appropriate flags and call +* qla2x00_loop_resync. If successful loop will be resynced +* Arguments : scsi_qla_host_t pointer +* returm : Success or Failure +*/ + +int qla2x00_perform_loop_resync(scsi_qla_host_t *ha) +{ + int32_t rval = 0; + + if (!test_and_set_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags)) { + /*Configure the flags so that resync happens properly*/ + atomic_set(&ha->loop_down_timer, 0); + if (!(ha->device_flags & DFLG_NO_CABLE)) { + atomic_set(&ha->loop_state, LOOP_UP); + set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); + set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); + set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); + + rval = qla2x00_loop_resync(ha); + } else + atomic_set(&ha->loop_state, LOOP_DEAD); + + clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags); + } + + return rval; +} + void -qla2x00_update_fcports(scsi_qla_host_t *ha) +qla2x00_update_fcports(scsi_qla_host_t *base_vha) { fc_port_t *fcport; + struct scsi_qla_host *vha; + struct qla_hw_data *ha = base_vha->hw; + unsigned long flags; + spin_lock_irqsave(&ha->vport_slock, flags); /* Go with deferred removal of rport references. */ - list_for_each_entry_rcu(fcport, &ha->fcports, list) { - if (fcport->drport) - qla2x00_rport_del(fcport); + list_for_each_entry(vha, &base_vha->hw->vp_list, list) { + atomic_inc(&vha->vref_count); + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { + if (fcport->drport && + atomic_read(&fcport->state) != FCS_UNCONFIGURED) { + spin_unlock_irqrestore(&ha->vport_slock, flags); + + qla2x00_rport_del(fcport); + + spin_lock_irqsave(&ha->vport_slock, flags); + } + } + atomic_dec(&vha->vref_count); + } + spin_unlock_irqrestore(&ha->vport_slock, flags); +} + +/* +* qla82xx_quiescent_state_cleanup +* Description: This function will block the new I/Os +* Its not aborting any I/Os as context +* is not destroyed during quiescence +* Arguments: scsi_qla_host_t +* return : void +*/ +void +qla82xx_quiescent_state_cleanup(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *vp; + + ql_dbg(ql_dbg_p3p, vha, 0xb002, + "Performing ISP error recovery - ha=%p.\n", ha); + + atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + qla2x00_mark_all_devices_lost(vha, 0); + list_for_each_entry(vp, &ha->vp_list, list) + qla2x00_mark_all_devices_lost(vha, 0); + } else { + if (!atomic_read(&vha->loop_down_timer)) + atomic_set(&vha->loop_down_timer, + LOOP_DOWN_TIME); + } + /* Wait for pending cmds to complete */ + qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST); +} + +void +qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *vp; + unsigned long flags; + fc_port_t *fcport; + + /* For ISP82XX driver waits for completion of the commands + * online flag should be set. + */ + if (!IS_QLA82XX(ha)) + vha->flags.online = 0; + ha->flags.chip_reset_done = 0; + clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + vha->qla_stats.total_isp_aborts++; + + ql_log(ql_log_info, vha, 0x00af, + "Performing ISP error recovery - ha=%p.\n", ha); + + /* For ISP82XX reset_chip is just disabling an interrupts + * Driver waits for the completion of the commands, + * the interrupts needs to be enabled. + */ + if (!IS_QLA82XX(ha)) + ha->isp_ops->reset_chip(vha); + + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + qla2x00_mark_all_devices_lost(vha, 0); + + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + atomic_inc(&vp->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); + + qla2x00_mark_all_devices_lost(vp, 0); + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vp->vref_count); + } + spin_unlock_irqrestore(&ha->vport_slock, flags); + } else { + if (!atomic_read(&vha->loop_down_timer)) + atomic_set(&vha->loop_down_timer, + LOOP_DOWN_TIME); + } + + /* Clear all async request states across all VPs. */ + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) + fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + atomic_inc(&vp->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); + + list_for_each_entry_rcu(fcport, &vp->vp_fcports, list) + fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vp->vref_count); + } + spin_unlock_irqrestore(&ha->vport_slock, flags); + + if (!ha->flags.eeh_busy) { + /* Make sure for ISP 82XX IO DMA is complete */ + if (IS_QLA82XX(ha)) { + qla82xx_chip_reset_cleanup(vha); + ql_log(ql_log_info, vha, 0x00b4, + "Done chip reset cleanup.\n"); + + /* Done waiting for pending commands + * Reset the online flag. + */ + vha->flags.online = 0; + } + + /* Requeue all commands in outstanding command list. */ + qla2x00_abort_all_cmds(vha, DID_RESET << 16); } } @@ -3392,127 +4070,135 @@ qla2x00_update_fcports(scsi_qla_host_t *ha) * 0 = success */ int -qla2x00_abort_isp(scsi_qla_host_t *ha) +qla2x00_abort_isp(scsi_qla_host_t *vha) { int rval; uint8_t status = 0; + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *vp; + struct req_que *req = ha->req_q_map[0]; + unsigned long flags; - if (ha->flags.online) { - ha->flags.online = 0; - clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + if (vha->flags.online) { + qla2x00_abort_isp_cleanup(vha); - qla_printk(KERN_INFO, ha, - "Performing ISP abort - ha= %p.\n", ha); - ha->isp_ops->reset_chip(ha); - - atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); - if (atomic_read(&ha->loop_state) != LOOP_DOWN) { - atomic_set(&ha->loop_state, LOOP_DOWN); - qla2x00_mark_all_devices_lost(ha, 0); - } else { - if (!atomic_read(&ha->loop_down_timer)) - atomic_set(&ha->loop_down_timer, - LOOP_DOWN_TIME); + if (unlikely(pci_channel_offline(ha->pdev) && + ha->flags.pci_channel_io_perm_failure)) { + clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + status = 0; + return status; } - /* Requeue all commands in outstanding command list. */ - qla2x00_abort_all_cmds(ha, DID_RESET << 16); + ha->isp_ops->get_flash_version(vha, req->ring); - ha->isp_ops->get_flash_version(ha, ha->request_ring); + ha->isp_ops->nvram_config(vha); - ha->isp_ops->nvram_config(ha); + if (!qla2x00_restart_isp(vha)) { + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); - if (!qla2x00_restart_isp(ha)) { - clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - - if (!atomic_read(&ha->loop_down_timer)) { + if (!atomic_read(&vha->loop_down_timer)) { /* * Issue marker command only when we are going * to start the I/O . */ - ha->marker_needed = 1; + vha->marker_needed = 1; } - ha->flags.online = 1; + vha->flags.online = 1; #ifdef CONFIG_SCSI_QLA2XXX_TARGET /* Enable target response to SCSI bus. */ - if (qla_tgt_mode_enabled(ha)) - qla2x00_send_enable_lun(ha, true); + if (qla_tgt_mode_enabled(vha)) + qla2x00_send_enable_lun(vha, true); #endif ha->isp_ops->enable_intrs(ha); ha->isp_abort_cnt = 0; - clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags); - - if (ha->eft) { - memset(ha->eft, 0, EFT_SIZE); - rval = qla2x00_enable_eft_trace(ha, - ha->eft_dma, EFT_NUM_BUFFERS); - if (rval) { - qla_printk(KERN_WARNING, ha, - "Unable to reinitialize EFT " - "(%d).\n", rval); - } - } + clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + if (IS_QLA81XX(ha) || IS_QLA8031(ha)) + qla2x00_get_fw_version(vha); if (ha->fce) { ha->flags.fce_enabled = 1; memset(ha->fce, 0, fce_calc_size(ha->fce_bufs)); - rval = qla2x00_enable_fce_trace(ha, + rval = qla2x00_enable_fce_trace(vha, ha->fce_dma, ha->fce_bufs, ha->fce_mb, &ha->fce_bufs); if (rval) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x8033, "Unable to reinitialize FCE " "(%d).\n", rval); ha->flags.fce_enabled = 0; } } + + if (ha->eft) { + memset(ha->eft, 0, EFT_SIZE); + rval = qla2x00_enable_eft_trace(vha, + ha->eft_dma, EFT_NUM_BUFFERS); + if (rval) { + ql_log(ql_log_warn, vha, 0x8034, + "Unable to reinitialize EFT " + "(%d).\n", rval); + } + } } else { /* failed the ISP abort */ - ha->flags.online = 1; - if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) { + vha->flags.online = 1; + if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { if (ha->isp_abort_cnt == 0) { - qla_printk(KERN_WARNING, ha, - "ISP error recovery failed - " - "board disabled\n"); + ql_log(ql_log_fatal, vha, 0x8035, + "ISP error recover failed - " + "board disabled.\n"); /* * The next call disables the board * completely. */ - ha->isp_ops->reset_adapter(ha); - ha->flags.online = 0; + ha->isp_ops->reset_adapter(vha); + vha->flags.online = 0; clear_bit(ISP_ABORT_RETRY, - &ha->dpc_flags); + &vha->dpc_flags); status = 0; } else { /* schedule another ISP abort */ ha->isp_abort_cnt--; - DEBUG(printk("qla%ld: ISP abort - " - "retry remaining %d\n", - ha->host_no, ha->isp_abort_cnt)); + ql_dbg(ql_dbg_taskm, vha, 0x8020, + "ISP abort - retry remaining %d.\n", + ha->isp_abort_cnt); status = 1; } } else { ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT; - DEBUG(printk("qla2x00(%ld): ISP error recovery " - "- retrying (%d) more times\n", - ha->host_no, ha->isp_abort_cnt)); - set_bit(ISP_ABORT_RETRY, &ha->dpc_flags); + ql_dbg(ql_dbg_taskm, vha, 0x8021, + "ISP error recovery - retrying (%d) " + "more times.\n", ha->isp_abort_cnt); + set_bit(ISP_ABORT_RETRY, &vha->dpc_flags); status = 1; } } } - if (status) { - qla_printk(KERN_INFO, ha, - "qla2x00_abort_isp: **** FAILED ****\n"); + if (!status) { + ql_dbg(ql_dbg_taskm, vha, 0x8022, "%s succeeded.\n", __func__); + + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + if (vp->vp_idx) { + atomic_inc(&vp->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); + + qla2x00_vp_abort_isp(vp); + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vp->vref_count); + } + } + spin_unlock_irqrestore(&ha->vport_slock, flags); + } else { - DEBUG(printk(KERN_INFO - "qla2x00_abort_isp(%ld): exiting.\n", - ha->host_no)); + ql_log(ql_log_warn, vha, 0x8023, "%s **** FAILED ****.\n", + __func__); } return(status); @@ -3529,51 +4215,119 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) * 0 = success */ static int -qla2x00_restart_isp(scsi_qla_host_t *ha) +qla2x00_restart_isp(scsi_qla_host_t *vha) { - uint8_t status = 0; + int status = 0; uint32_t wait_time; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; /* If firmware needs to be loaded */ - if (qla2x00_isp_firmware(ha)) { - ha->flags.online = 0; - if (!(status = ha->isp_ops->chip_diag(ha))) - status = qla2x00_setup_chip(ha); + if (qla2x00_isp_firmware(vha)) { + vha->flags.online = 0; + status = ha->isp_ops->chip_diag(vha); + if (!status) + status = qla2x00_setup_chip(vha); } - if (!status && !(status = qla2x00_init_rings(ha))) { - clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - if (!(status = qla2x00_fw_ready(ha))) { - DEBUG(printk("%s(): Start configure loop, " - "status = %d\n", __func__, status)); + if (!status && !(status = qla2x00_init_rings(vha))) { + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); + ha->flags.chip_reset_done = 1; + /* Initialize the queues in use */ + qla25xx_init_queues(ha); + + status = qla2x00_fw_ready(vha); + if (!status) { +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + unsigned long flags; +#endif + ql_dbg(ql_dbg_taskm, vha, 0x8031, + "Start configure loop status = %d.\n", status); /* Issue a marker after FW becomes ready. */ - qla2x00_marker(ha, 0, 0, MK_SYNC_ALL); + qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); + + vha->flags.online = 1; + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + spin_lock_irqsave(&ha->hardware_lock, flags); + if (ha->atio_ignored) { + ql_dbg(ql_dbg_tgt, vha, 0xffff, "%s(%ld): " + "Process delayed ATIO IOCBs\n", + __func__, vha->host_no); + ha->atio_ignored = 0; + if (likely(IS_FWI2_CAPABLE(ha))) + qla24xx_process_atio_queue(vha); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); +#endif - ha->flags.online = 1; /* Wait at most MAX_TARGET RSCNs for a stable link. */ wait_time = 256; do { - clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); - qla2x00_configure_loop(ha); + clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + qla2x00_configure_loop(vha); wait_time--; - } while (!atomic_read(&ha->loop_down_timer) && - !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) && - wait_time && - (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))); + } while (!atomic_read(&vha->loop_down_timer) && + !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) + && wait_time && (test_bit(LOOP_RESYNC_NEEDED, + &vha->dpc_flags))); } - /* if no cable then assume it's good */ - if ((ha->device_flags & DFLG_NO_CABLE)) + /* if no cable or FW not active, then assume it's good */ + if ((vha->device_flags & DFLG_NO_CABLE) || !qla_firmware_active(vha)) status = 0; - DEBUG(printk("%s(): Configure loop done, status = 0x%x\n", - __func__, - status)); + ql_dbg(ql_dbg_taskm, vha, 0x8032, + "Configure loop done, status = 0x%x.\n", status); } return (status); } +static int +qla25xx_init_queues(struct qla_hw_data *ha) +{ + struct rsp_que *rsp = NULL; + struct req_que *req = NULL; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + int ret = -1; + int i; + + for (i = 1; i < ha->max_rsp_queues; i++) { + rsp = ha->rsp_q_map[i]; + if (rsp) { + rsp->options &= ~BIT_0; + ret = qla25xx_init_rsp_que(base_vha, rsp); + if (ret != QLA_SUCCESS) + ql_dbg(ql_dbg_init, base_vha, 0x00ff, + "%s Rsp que: %d init failed.\n", + __func__, rsp->id); + else + ql_dbg(ql_dbg_init, base_vha, 0x0100, + "%s Rsp que: %d inited.\n", + __func__, rsp->id); + } + } + for (i = 1; i < ha->max_req_queues; i++) { + req = ha->req_q_map[i]; + if (req) { + /* Clear outstanding commands array. */ + req->options &= ~BIT_0; + ret = qla25xx_init_req_que(base_vha, req); + if (ret != QLA_SUCCESS) + ql_dbg(ql_dbg_init, base_vha, 0x0101, + "%s Req que: %d init failed.\n", + __func__, req->id); + else + ql_dbg(ql_dbg_init, base_vha, 0x0102, + "%s Req que: %d inited.\n", + __func__, req->id); + } + } + return ret; +} + /* * qla2x00_reset_adapter * Reset adapter. @@ -3582,12 +4336,13 @@ qla2x00_restart_isp(scsi_qla_host_t *ha) * ha = adapter block pointer. */ void -qla2x00_reset_adapter(scsi_qla_host_t *ha) +qla2x00_reset_adapter(scsi_qla_host_t *vha) { unsigned long flags = 0; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - ha->flags.online = 0; + vha->flags.online = 0; ha->isp_ops->disable_intrs(ha); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -3599,12 +4354,16 @@ qla2x00_reset_adapter(scsi_qla_host_t *ha) } void -qla24xx_reset_adapter(scsi_qla_host_t *ha) +qla24xx_reset_adapter(scsi_qla_host_t *vha) { unsigned long flags = 0; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - ha->flags.online = 0; + if (IS_QLA82XX(ha)) + return; + + vha->flags.online = 0; ha->isp_ops->disable_intrs(ha); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -3613,14 +4372,19 @@ qla24xx_reset_adapter(scsi_qla_host_t *ha) WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); RD_REG_DWORD(®->hccr); spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (IS_NOPOLLING_TYPE(ha)) + ha->isp_ops->enable_intrs(ha); } /* On sparc systems, obtain port and node WWN from firmware * properties. */ -static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, struct nvram_24xx *nv) +static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *vha, + struct nvram_24xx *nv) { #ifdef CONFIG_SPARC + struct qla_hw_data *ha = vha->hw; struct pci_dev *pdev = ha->pdev; struct device_node *dp = pci_device_to_OF_node(pdev); const u8 *val; @@ -3637,7 +4401,7 @@ static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, struct nvram_24xx *n } int -qla24xx_nvram_config(scsi_qla_host_t *ha) +qla24xx_nvram_config(scsi_qla_host_t *vha) { int rval; struct init_cb_24xx *icb; @@ -3646,46 +4410,53 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) uint8_t *dptr1, *dptr2; uint32_t chksum; uint16_t cnt; + struct qla_hw_data *ha = vha->hw; rval = QLA_SUCCESS; icb = (struct init_cb_24xx *)ha->init_cb; nv = ha->nvram; /* Determine NVRAM starting address. */ - ha->nvram_size = sizeof(struct nvram_24xx); - ha->nvram_base = FA_NVRAM_FUNC0_ADDR; - ha->vpd_size = FA_NVRAM_VPD_SIZE; - ha->vpd_base = FA_NVRAM_VPD0_ADDR; - if (PCI_FUNC(ha->pdev->devfn)) { + if (ha->flags.port0) { + ha->nvram_base = FA_NVRAM_FUNC0_ADDR; + ha->vpd_base = FA_NVRAM_VPD0_ADDR; + } else { ha->nvram_base = FA_NVRAM_FUNC1_ADDR; ha->vpd_base = FA_NVRAM_VPD1_ADDR; } + ha->nvram_size = sizeof(struct nvram_24xx); + ha->vpd_size = FA_NVRAM_VPD_SIZE; + if (IS_QLA82XX(ha)) + ha->vpd_size = FA_VPD_SIZE_82XX; /* Get VPD data into cache */ ha->vpd = ha->nvram + VPD_OFFSET; - ha->isp_ops->read_nvram(ha, (uint8_t *)ha->vpd, + ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd, ha->nvram_base - FA_NVRAM_FUNC0_ADDR, FA_NVRAM_VPD_SIZE * 4); /* Get NVRAM data into cache and calculate checksum. */ dptr = (uint32_t *)nv; - ha->isp_ops->read_nvram(ha, (uint8_t *)dptr, ha->nvram_base, + ha->isp_ops->read_nvram(vha, (uint8_t *)dptr, ha->nvram_base, ha->nvram_size); for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++) chksum += le32_to_cpu(*dptr++); - DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size)); + ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x006a, + "Contents of NVRAM\n"); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010d, + (uint8_t *)nv, ha->nvram_size); /* Bad NVRAM data, set defaults parameters. */ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) { /* Reset NVRAM data. */ - qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: " - "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0], - le16_to_cpu(nv->nvram_version)); - qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " - "invalid -- WWPN) defaults.\n"); + ql_log(ql_log_warn, vha, 0x006b, + "Inconsistent NVRAM detected: checksum=0x%x id=%c " + "version=0x%x.\n", chksum, nv->id[0], nv->nvram_version); + ql_log(ql_log_warn, vha, 0x006c, + "Falling back to functioning (yet invalid -- WWPN) " + "defaults.\n"); /* * Set default initialization control block. @@ -3698,7 +4469,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) nv->exchange_count = __constant_cpu_to_le16(0); nv->hard_address = __constant_cpu_to_le16(124); nv->port_name[0] = 0x21; - nv->port_name[1] = 0x00 + PCI_FUNC(ha->pdev->devfn); + nv->port_name[1] = 0x00 + ha->port_no; nv->port_name[2] = 0x00; nv->port_name[3] = 0xe0; nv->port_name[4] = 0x8b; @@ -3713,7 +4484,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) nv->node_name[5] = 0x1c; nv->node_name[6] = 0x55; nv->node_name[7] = 0x86; - qla24xx_nvram_wwn_from_ofw(ha, nv); + qla24xx_nvram_wwn_from_ofw(vha, nv); nv->login_retry_count = __constant_cpu_to_le16(8); nv->interrupt_delay_timer = __constant_cpu_to_le16(0); nv->login_timeout = __constant_cpu_to_le16(0); @@ -3734,7 +4505,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) #ifdef CONFIG_SCSI_QLA2XXX_TARGET /* Check if target mode enabled */ - if (qla_tgt_mode_enabled(ha)) { + if (qla_tgt_mode_enabled(vha)) { if (!ha->saved_set) { /* We save only once */ ha->saved_exchange_count = nv->exchange_count; @@ -3750,15 +4521,24 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4); /* Disable ini mode, if requested */ - if (!qla_ini_mode_enabled(ha)) + if (!qla_ini_mode_enabled(vha)) nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_5); - /* Disable Full Login after LIP */ - nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + if (ha->enable_explicit_conf) { + ql_log(ql_log_info, vha, 0x1073, "Enabling FC tape support\n"); + /* Disable Full Login after LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + /* Enable FC tapes support */ + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); + } else { + ql_log(ql_log_info, vha, 0x1073, "Disabling FC tape support\n"); + /* Enable Full Login after LIP */ + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_13); + /* Disable FC tapes support */ + nv->firmware_options_2 &= __constant_cpu_to_le32(~BIT_12); + } /* Enable initial LIP */ nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9); - /* Enable FC tapes support */ - nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); } else { if (ha->saved_set) { nv->exchange_count = ha->saved_exchange_count; @@ -3767,19 +4547,20 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) nv->firmware_options_3 = ha->saved_firmware_options_3; } } -#endif /* out-of-order frames reassembly */ nv->firmware_options_3 |= __constant_cpu_to_le32(BIT_6|BIT_9); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + if (ha->enable_class_2) { - if (ha->flags.init_done) { - fc_host_supported_classes(ha->host) = + if (vha->flags.init_done) { + fc_host_supported_classes(vha->host) = FC_COS_CLASS2 | FC_COS_CLASS3; } nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8); } else { - if (ha->flags.init_done) - fc_host_supported_classes(ha->host) = FC_COS_CLASS3; + if (vha->flags.init_done) + fc_host_supported_classes(vha->host) = FC_COS_CLASS3; nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8); } @@ -3792,7 +4573,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) #endif /* Reset Initialization control block */ - memset(icb, 0, sizeof(struct init_cb_24xx)); + memset(icb, 0, ha->init_cb_size); /* Copy 1st segment. */ dptr1 = (uint8_t *)icb; @@ -3815,13 +4596,15 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) /* * Setup driver NVRAM options. */ - qla2x00_set_model_info(ha, nv->model_name, sizeof(nv->model_name), + qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name), "QLA2462"); - /* Prepare nodename */ #ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (ha->node_name_set) { - memcpy(icb->node_name, ha->tgt_node_name, WWN_SIZE); + if (vha->node_name_set || vha->port_name_set) { + if (vha->node_name_set) + memcpy(icb->node_name, vha->node_name, WWN_SIZE); + if (vha->port_name_set) + memcpy(icb->port_name, vha->port_name, WWN_SIZE); icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14); } else #endif @@ -3831,6 +4614,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); } + /* Prepare nodename */ if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) { /* * Firmware will apply the following mask if the nodename was @@ -3860,8 +4644,8 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) ha->serial0 = icb->port_name[5]; ha->serial1 = icb->port_name[6]; ha->serial2 = icb->port_name[7]; - ha->node_name = icb->node_name; - ha->port_name = icb->port_name; + memcpy(vha->node_name, icb->node_name, WWN_SIZE); + memcpy(vha->port_name, icb->port_name, WWN_SIZE); icb->execution_throttle = __constant_cpu_to_le16(0xFFFF); @@ -3916,76 +4700,82 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) ha->login_retry_count = ql2xloginretrycount; /* Enable ZIO. */ - if (!ha->flags.init_done) { + if (!vha->flags.init_done) { +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->zio_mode = QLA_ZIO_DISABLED; +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ ha->zio_mode = le32_to_cpu(icb->firmware_options_2) & (BIT_3 | BIT_2 | BIT_1 | BIT_0); ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ? le16_to_cpu(icb->interrupt_delay_timer): 2; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ } icb->firmware_options_2 &= __constant_cpu_to_le32( ~(BIT_3 | BIT_2 | BIT_1 | BIT_0)); - ha->flags.process_response_queue = 0; + vha->flags.process_response_queue = 0; if (ha->zio_mode != QLA_ZIO_DISABLED) { ha->zio_mode = QLA_ZIO_MODE_6; - DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay " - "(%d us).\n", ha->host_no, ha->zio_mode, - ha->zio_timer * 100)); - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x006f, "ZIO mode %d enabled; timer delay (%d us).\n", ha->zio_mode, ha->zio_timer * 100); icb->firmware_options_2 |= cpu_to_le32( (uint32_t)ha->zio_mode); icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer); - ha->flags.process_response_queue = 1; + vha->flags.process_response_queue = 1; } if (rval) { - DEBUG2_3(printk(KERN_WARNING - "scsi(%ld): NVRAM configuration failed!\n", ha->host_no)); + ql_log(ql_log_warn, vha, 0x0070, + "NVRAM configuration failed.\n"); } return (rval); } static int -qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr) +qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, + uint32_t faddr) { - int rval; + int rval = QLA_SUCCESS; int segments, fragment; - uint32_t faddr; uint32_t *dcode, dlen; uint32_t risc_addr; uint32_t risc_size; uint32_t i; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + ql_dbg(ql_dbg_init, vha, 0x008b, + "FW: Loading firmware from flash (%x).\n", faddr); rval = QLA_SUCCESS; segments = FA_RISC_CODE_SEGMENTS; - faddr = FA_RISC_CODE_ADDR; - dcode = (uint32_t *)ha->request_ring; + dcode = (uint32_t *)req->ring; *srisc_addr = 0; /* Validate firmware image by checking version. */ - qla24xx_read_flash_data(ha, dcode, faddr + 4, 4); + qla24xx_read_flash_data(vha, dcode, faddr + 4, 4); for (i = 0; i < 4; i++) dcode[i] = be32_to_cpu(dcode[i]); if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff && dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) || (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of flash firmware image!\n"); - qla_printk(KERN_WARNING, ha, - "Firmware data: %08x %08x %08x %08x!\n", dcode[0], - dcode[1], dcode[2], dcode[3]); + ql_log(ql_log_fatal, vha, 0x008c, + "Unable to verify the integrity of flash firmware " + "image.\n"); + ql_log(ql_log_fatal, vha, 0x008d, + "Firmware data: %08x %08x %08x %08x.\n", + dcode[0], dcode[1], dcode[2], dcode[3]); return QLA_FUNCTION_FAILED; } while (segments && rval == QLA_SUCCESS) { /* Read segment's load information. */ - qla24xx_read_flash_data(ha, dcode, faddr, 4); + qla24xx_read_flash_data(vha, dcode, faddr, 4); risc_addr = be32_to_cpu(dcode[2]); *srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr; @@ -3997,23 +4787,21 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr) if (dlen > risc_size) dlen = risc_size; - DEBUG7(printk("scsi(%ld): Loading risc segment@ risc " - "addr %x, number of dwords 0x%x, offset 0x%x.\n", - ha->host_no, risc_addr, dlen, faddr)); + ql_dbg(ql_dbg_init, vha, 0x008e, + "Loading risc segment@ risc addr %x " + "number of dwords 0x%x offset 0x%x.\n", + risc_addr, dlen, faddr); - qla24xx_read_flash_data(ha, dcode, faddr, dlen); + qla24xx_read_flash_data(vha, dcode, faddr, dlen); for (i = 0; i < dlen; i++) dcode[i] = swab32(dcode[i]); - rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr, + rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen); if (rval) { - DEBUG(printk("scsi(%ld):[ERROR] Failed to load " - "segment %d of firmware\n", ha->host_no, - fragment)); - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to load segment %d of " - "firmware\n", fragment); + ql_log(ql_log_fatal, vha, 0x008f, + "Failed to load segment %d of firmware.\n", + fragment); break; } @@ -4033,34 +4821,37 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr) #define QLA_FW_URL "http://ldriver.qlogic.com/firmware/" int -qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) +qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) { int rval; int i, fragment; uint16_t *wcode, *fwcode; uint32_t risc_addr, risc_size, fwclen, wlen, *seg; struct fw_blob *blob; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; /* Load firmware blob. */ - blob = qla2x00_request_firmware(ha); + blob = qla2x00_request_firmware(vha); if (!blob) { - qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n"); - qla_printk(KERN_ERR, ha, "Firmware images can be retrieved " - "from: " QLA_FW_URL ".\n"); + ql_log(ql_log_info, vha, 0x0083, + "Fimware image unavailable.\n"); + ql_log(ql_log_info, vha, 0x0084, + "Firmware images can be retrieved from: "QLA_FW_URL ".\n"); return QLA_FUNCTION_FAILED; } rval = QLA_SUCCESS; - wcode = (uint16_t *)ha->request_ring; + wcode = (uint16_t *)req->ring; *srisc_addr = 0; fwcode = (uint16_t *)blob->fw->data; fwclen = 0; /* Validate firmware image by checking version. */ if (blob->fw->size < 8 * sizeof(uint16_t)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image (%Zd)!\n", + ql_log(ql_log_fatal, vha, 0x0085, + "Unable to verify integrity of firmware image (%Zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -4069,11 +4860,11 @@ qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) if ((wcode[0] == 0xffff && wcode[1] == 0xffff && wcode[2] == 0xffff && wcode[3] == 0xffff) || (wcode[0] == 0 && wcode[1] == 0 && wcode[2] == 0 && wcode[3] == 0)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image!\n"); - qla_printk(KERN_WARNING, ha, - "Firmware data: %04x %04x %04x %04x!\n", wcode[0], - wcode[1], wcode[2], wcode[3]); + ql_log(ql_log_fatal, vha, 0x0086, + "Unable to verify integrity of firmware image.\n"); + ql_log(ql_log_fatal, vha, 0x0087, + "Firmware data: %04x %04x %04x %04x.\n", + wcode[0], wcode[1], wcode[2], wcode[3]); goto fail_fw_integrity; } @@ -4086,9 +4877,9 @@ qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) /* Validate firmware image size. */ fwclen += risc_size * sizeof(uint16_t); if (blob->fw->size < fwclen) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_fatal, vha, 0x0088, "Unable to verify integrity of firmware image " - "(%Zd)!\n", blob->fw->size); + "(%Zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -4097,23 +4888,19 @@ qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) wlen = (uint16_t)(ha->fw_transfer_size >> 1); if (wlen > risc_size) wlen = risc_size; - - DEBUG7(printk("scsi(%ld): Loading risc segment@ risc " - "addr %x, number of words 0x%x.\n", ha->host_no, - risc_addr, wlen)); + ql_dbg(ql_dbg_init, vha, 0x0089, + "Loading risc segment@ risc addr %x number of " + "words 0x%x.\n", risc_addr, wlen); for (i = 0; i < wlen; i++) wcode[i] = swab16(fwcode[i]); - rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr, + rval = qla2x00_load_ram(vha, req->dma, risc_addr, wlen); if (rval) { - DEBUG(printk("scsi(%ld):[ERROR] Failed to load " - "segment %d of firmware\n", ha->host_no, - fragment)); - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to load segment %d of " - "firmware\n", fragment); + ql_log(ql_log_fatal, vha, 0x008a, + "Failed to load segment %d of firmware.\n", + fragment); break; } @@ -4140,21 +4927,25 @@ fail_fw_integrity: * host_reset, bring up w/ Target Mode Enabled */ void -qla2x00_enable_tgt_mode(scsi_qla_host_t *ha) +qla2x00_enable_tgt_mode(scsi_qla_host_t *vha) { unsigned long flags; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - spin_lock_irqsave(&pha->hardware_lock, flags); - qla_set_tgt_mode(ha); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); + qla_set_tgt_mode(vha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (ha->parent) - ha = ha->parent; - - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - qla2x00_wait_for_hba_online(ha); + if (vha->vp_idx != 0) + qla24xx_enable_vp(vha); + else { + BUG_ON(vha != base_vha); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_hba_online(vha); + } + return; } EXPORT_SYMBOL(qla2x00_enable_tgt_mode); @@ -4164,18 +4955,25 @@ EXPORT_SYMBOL(qla2x00_enable_tgt_mode); * Disable Target Mode and reset the adapter */ void -qla2x00_disable_tgt_mode(scsi_qla_host_t *ha) +qla2x00_disable_tgt_mode(scsi_qla_host_t *vha) { unsigned long flags; - scsi_qla_host_t *vha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - spin_lock_irqsave(&vha->hardware_lock, flags); - qla_clear_tgt_mode(ha); - spin_unlock_irqrestore(&vha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); + qla_clear_tgt_mode(vha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); - set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); - qla2x00_wait_for_hba_online(vha); + if (vha->vp_idx != 0) + qla24xx_disable_vp(vha); + else { + BUG_ON(vha != base_vha); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_hba_online(vha); + } + return; } EXPORT_SYMBOL(qla2x00_disable_tgt_mode); @@ -4185,18 +4983,18 @@ EXPORT_SYMBOL(qla2x00_disable_tgt_mode); * Caller CAN have hardware lock held as specified by ha_locked parameter. * Might release it, then reacquire. */ -int qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked) +int qla2x00_issue_marker(scsi_qla_host_t *vha, int ha_locked) { if (ha_locked) { - if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != - QLA_SUCCESS) + if (__qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0, + MK_SYNC_ALL) != QLA_SUCCESS) return QLA_FUNCTION_FAILED; } else { - if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != - QLA_SUCCESS) + if (__qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0, + MK_SYNC_ALL) != QLA_SUCCESS) return QLA_FUNCTION_FAILED; } - ha->marker_needed = 0; + vha->marker_needed = 0; return QLA_SUCCESS; } EXPORT_SYMBOL(qla2x00_issue_marker); @@ -4240,8 +5038,8 @@ EXPORT_SYMBOL_GPL(qla2xxx_tgt_unregister_driver); #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ -int -qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) +static int +qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) { int rval; int segments, fragment; @@ -4251,32 +5049,36 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) uint32_t i; struct fw_blob *blob; uint32_t *fwcode, fwclen; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; /* Load firmware blob. */ - blob = qla2x00_request_firmware(ha); + blob = qla2x00_request_firmware(vha); if (!blob) { - qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n"); - qla_printk(KERN_ERR, ha, "Firmware images can be retrieved " - "from: " QLA_FW_URL ".\n"); + ql_log(ql_log_warn, vha, 0x0090, + "Fimware image unavailable.\n"); + ql_log(ql_log_warn, vha, 0x0091, + "Firmware images can be retrieved from: " + QLA_FW_URL ".\n"); - /* Try to load RISC code from flash. */ - qla_printk(KERN_ERR, ha, "Attempting to load (potentially " - "outdated) firmware from flash.\n"); - return qla24xx_load_risc_flash(ha, srisc_addr); + return QLA_FUNCTION_FAILED; } + ql_dbg(ql_dbg_init, vha, 0x0092, + "FW: Loading via request-firmware.\n"); + rval = QLA_SUCCESS; segments = FA_RISC_CODE_SEGMENTS; - dcode = (uint32_t *)ha->request_ring; + dcode = (uint32_t *)req->ring; *srisc_addr = 0; fwcode = (uint32_t *)blob->fw->data; fwclen = 0; /* Validate firmware image by checking version. */ if (blob->fw->size < 8 * sizeof(uint32_t)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image (%Zd)!\n", + ql_log(ql_log_fatal, vha, 0x0093, + "Unable to verify integrity of firmware image (%Zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -4286,11 +5088,12 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) || (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image!\n"); - qla_printk(KERN_WARNING, ha, - "Firmware data: %08x %08x %08x %08x!\n", dcode[0], - dcode[1], dcode[2], dcode[3]); + ql_log(ql_log_fatal, vha, 0x0094, + "Unable to verify integrity of firmware image (%Zd).\n", + blob->fw->size); + ql_log(ql_log_fatal, vha, 0x0095, + "Firmware data: %08x %08x %08x %08x.\n", + dcode[0], dcode[1], dcode[2], dcode[3]); goto fail_fw_integrity; } @@ -4302,9 +5105,9 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) /* Validate firmware image size. */ fwclen += risc_size * sizeof(uint32_t); if (blob->fw->size < fwclen) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_fatal, vha, 0x0096, "Unable to verify integrity of firmware image " - "(%Zd)!\n", blob->fw->size); + "(%Zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -4315,22 +5118,19 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) if (dlen > risc_size) dlen = risc_size; - DEBUG7(printk("scsi(%ld): Loading risc segment@ risc " - "addr %x, number of dwords 0x%x.\n", ha->host_no, - risc_addr, dlen)); + ql_dbg(ql_dbg_init, vha, 0x0097, + "Loading risc segment@ risc addr %x " + "number of dwords 0x%x.\n", risc_addr, dlen); for (i = 0; i < dlen; i++) dcode[i] = swab32(fwcode[i]); - rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr, + rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen); if (rval) { - DEBUG(printk("scsi(%ld):[ERROR] Failed to load " - "segment %d of firmware\n", ha->host_no, - fragment)); - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to load segment %d of " - "firmware\n", fragment); + ql_log(ql_log_fatal, vha, 0x0098, + "Failed to load segment %d of firmware.\n", + fragment); break; } @@ -4349,63 +5149,139 @@ fail_fw_integrity: return QLA_FUNCTION_FAILED; } +int +qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + + if (ql2xfwloadbin == 1) + return qla81xx_load_risc(vha, srisc_addr); + + /* + * FW Load priority: + * 1) Firmware via request-firmware interface (.bin file). + * 2) Firmware residing in flash. + */ + rval = qla24xx_load_risc_blob(vha, srisc_addr); + if (rval == QLA_SUCCESS) + return rval; + + return qla24xx_load_risc_flash(vha, srisc_addr, + vha->hw->flt_region_fw); +} + +int +qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + + if (ql2xfwloadbin == 1) + goto try_risc_fw; + + /* + * FW Load priority: + * 1) Firmware via request-firmware interface (.bin file). + * 2) Firmware residing in flash. + * 3) Golden-Firmware residing in flash -- limited operation. + */ + + rval = qla24xx_load_risc_blob(vha, srisc_addr); + if (rval == QLA_SUCCESS) + return rval; + +try_risc_fw: + rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_fw); + if (rval == QLA_SUCCESS || !ha->flt_region_gold_fw) + return rval; + + ql_log(ql_log_info, vha, 0x0099, + "Attempting to fallback to golden firmware.\n"); + rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_gold_fw); + if (rval != QLA_SUCCESS) + return rval; + + ql_log(ql_log_info, vha, 0x009a, "Update operational firmware.\n"); + ha->flags.running_gold_fw = 1; + return rval; +} + void -qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha) +qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha) { int ret, retries; + struct qla_hw_data *ha = vha->hw; + if (ha->flags.pci_channel_io_perm_failure) + return; if (!IS_FWI2_CAPABLE(ha)) return; if (!ha->fw_major_version) return; - ret = qla2x00_stop_firmware(ha); + ret = qla2x00_stop_firmware(vha); for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT && - retries ; retries--) { - qla2x00_reset_chip(ha); - if (qla2x00_chip_diag(ha) != QLA_SUCCESS) + ret != QLA_INVALID_COMMAND && retries ; retries--) { + ha->isp_ops->reset_chip(vha); + if (ha->isp_ops->chip_diag(vha) != QLA_SUCCESS) continue; - if (qla2x00_setup_chip(ha) != QLA_SUCCESS) + if (qla2x00_setup_chip(vha) != QLA_SUCCESS) continue; - qla_printk(KERN_INFO, ha, - "Attempting retry of stop-firmware command...\n"); - ret = qla2x00_stop_firmware(ha); + ql_log(ql_log_info, vha, 0x8015, + "Attempting retry of stop-firmware command.\n"); + ret = qla2x00_stop_firmware(vha); } } int -qla24xx_configure_vhba(scsi_qla_host_t *ha) +qla24xx_configure_vhba(scsi_qla_host_t *vha) { int rval = QLA_SUCCESS; + int rval2; uint16_t mb[MAILBOX_REGISTER_COUNT]; + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + struct req_que *req; + struct rsp_que *rsp; - if (!ha->parent) + if (!vha->vp_idx) return -EINVAL; - rval = qla2x00_fw_ready(ha->parent); + rval = qla2x00_fw_ready(base_vha); + if (ha->flags.cpu_affinity_enabled) + req = ha->req_q_map[0]; + else + req = vha->req; + rsp = req->rsp; + if (rval == QLA_SUCCESS) { - clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - qla2x00_marker(ha->parent, 0, 0, MK_SYNC_ALL); + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); + qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); } - ha->flags.management_server_logged_in = 0; + vha->flags.management_server_logged_in = 0; /* Login to SNS first */ - qla24xx_login_fabric(ha->parent, NPH_SNS, 0xff, 0xff, 0xfc, - mb, BIT_1); - if (mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG15(qla_printk(KERN_INFO, ha, - "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x " - "mb[2]=%x mb[6]=%x mb[7]=%x\n", NPH_SNS, - mb[0], mb[1], mb[2], mb[6], mb[7])); + rval2 = ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb, + BIT_1); + if (rval2 != QLA_SUCCESS || mb[0] != MBS_COMMAND_COMPLETE) { + if (rval2 == QLA_MEMORY_ALLOC_FAILED) + ql_dbg(ql_dbg_init, vha, 0x0120, + "Failed SNS login: loop_id=%x, rval2=%d\n", + NPH_SNS, rval2); + else + ql_dbg(ql_dbg_init, vha, 0x0103, + "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x " + "mb[2]=%x mb[6]=%x mb[7]=%x.\n", + NPH_SNS, mb[0], mb[1], mb[2], mb[6], mb[7]); return (QLA_FUNCTION_FAILED); } - atomic_set(&ha->loop_down_timer, 0); - atomic_set(&ha->loop_state, LOOP_UP); - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); - set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); - rval = qla2x00_loop_resync(ha->parent); + atomic_set(&vha->loop_down_timer, 0); + atomic_set(&vha->loop_state, LOOP_UP); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + rval = qla2x00_loop_resync(base_vha); return rval; } @@ -4416,9 +5292,10 @@ static LIST_HEAD(qla_cs84xx_list); static DEFINE_MUTEX(qla_cs84xx_mutex); static struct qla_chip_state_84xx * -qla84xx_get_chip(struct scsi_qla_host *ha) +qla84xx_get_chip(struct scsi_qla_host *vha) { struct qla_chip_state_84xx *cs84xx; + struct qla_hw_data *ha = vha->hw; mutex_lock(&qla_cs84xx_mutex); @@ -4458,24 +5335,674 @@ __qla84xx_chip_release(struct kref *kref) } void -qla84xx_put_chip(struct scsi_qla_host *ha) +qla84xx_put_chip(struct scsi_qla_host *vha) { + struct qla_hw_data *ha = vha->hw; if (ha->cs84xx) kref_put(&ha->cs84xx->kref, __qla84xx_chip_release); } static int -qla84xx_init_chip(scsi_qla_host_t *ha) +qla84xx_init_chip(scsi_qla_host_t *vha) { int rval; uint16_t status[2]; + struct qla_hw_data *ha = vha->hw; mutex_lock(&ha->cs84xx->fw_update_mutex); - rval = qla84xx_verify_chip(ha, status); + rval = qla84xx_verify_chip(vha, status); mutex_unlock(&ha->cs84xx->fw_update_mutex); return rval != QLA_SUCCESS || status[0] ? QLA_FUNCTION_FAILED: QLA_SUCCESS; } + +/* 81XX Support **************************************************************/ + +int +qla81xx_nvram_config(scsi_qla_host_t *vha) +{ + int rval; + struct init_cb_81xx *icb; + struct nvram_81xx *nv; + uint32_t *dptr; + uint8_t *dptr1, *dptr2; + uint32_t chksum; + uint16_t cnt; + struct qla_hw_data *ha = vha->hw; + + rval = QLA_SUCCESS; + icb = (struct init_cb_81xx *)ha->init_cb; + nv = ha->nvram; + + /* Determine NVRAM starting address. */ + ha->nvram_size = sizeof(struct nvram_81xx); + ha->vpd_size = FA_NVRAM_VPD_SIZE; + + /* Get VPD data into cache */ + ha->vpd = ha->nvram + VPD_OFFSET; + ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_vpd << 2, + ha->vpd_size); + + /* Get NVRAM data into cache and calculate checksum. */ + ha->isp_ops->read_optrom(vha, ha->nvram, ha->flt_region_nvram << 2, + ha->nvram_size); + dptr = (uint32_t *)nv; + for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++) + chksum += le32_to_cpu(*dptr++); + + ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0111, + "Contents of NVRAM:\n"); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0112, + (uint8_t *)nv, ha->nvram_size); + + /* Bad NVRAM data, set defaults parameters. */ + if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' + || nv->id[3] != ' ' || + nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) { + /* Reset NVRAM data. */ + ql_log(ql_log_info, vha, 0x0073, + "Inconsistent NVRAM detected: checksum=0x%x id=%c " + "version=0x%x.\n", chksum, nv->id[0], + le16_to_cpu(nv->nvram_version)); + ql_log(ql_log_info, vha, 0x0074, + "Falling back to functioning (yet invalid -- WWPN) " + "defaults.\n"); + + /* + * Set default initialization control block. + */ + memset(nv, 0, ha->nvram_size); + nv->nvram_version = __constant_cpu_to_le16(ICB_VERSION); + nv->version = __constant_cpu_to_le16(ICB_VERSION); + nv->frame_payload_size = __constant_cpu_to_le16(2048); + nv->execution_throttle = __constant_cpu_to_le16(0xFFFF); + nv->exchange_count = __constant_cpu_to_le16(0); + nv->port_name[0] = 0x21; + nv->port_name[1] = 0x00 + ha->port_no; + nv->port_name[2] = 0x00; + nv->port_name[3] = 0xe0; + nv->port_name[4] = 0x8b; + nv->port_name[5] = 0x1c; + nv->port_name[6] = 0x55; + nv->port_name[7] = 0x86; + nv->node_name[0] = 0x20; + nv->node_name[1] = 0x00; + nv->node_name[2] = 0x00; + nv->node_name[3] = 0xe0; + nv->node_name[4] = 0x8b; + nv->node_name[5] = 0x1c; + nv->node_name[6] = 0x55; + nv->node_name[7] = 0x86; + nv->login_retry_count = __constant_cpu_to_le16(8); + nv->interrupt_delay_timer = __constant_cpu_to_le16(0); + nv->login_timeout = __constant_cpu_to_le16(0); + nv->firmware_options_1 = + __constant_cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1); + nv->firmware_options_2 = __constant_cpu_to_le32(2 << 4); + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); + nv->firmware_options_3 = __constant_cpu_to_le32(2 << 13); + nv->host_p = __constant_cpu_to_le32(BIT_11|BIT_10); + nv->efi_parameters = __constant_cpu_to_le32(0); + nv->reset_delay = 5; + nv->max_luns_per_target = __constant_cpu_to_le16(128); + nv->port_down_retry_count = __constant_cpu_to_le16(30); + nv->link_down_timeout = __constant_cpu_to_le16(180); + nv->enode_mac[0] = 0x00; + nv->enode_mac[1] = 0xC0; + nv->enode_mac[2] = 0xDD; + nv->enode_mac[3] = 0x04; + nv->enode_mac[4] = 0x05; + nv->enode_mac[5] = 0x06 + ha->port_no; + + rval = 1; + } + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + /* Check if target mode enabled */ + if (qla_tgt_mode_enabled(vha)) { + if (!ha->saved_set) { + /* We save only once */ + ha->saved_exchange_count = nv->exchange_count; + ha->saved_firmware_options_1 = nv->firmware_options_1; + ha->saved_firmware_options_2 = nv->firmware_options_2; + ha->saved_firmware_options_3 = nv->firmware_options_3; + ha->saved_set = 1; + } + + nv->exchange_count = __constant_cpu_to_le16(0xFFFF); + + /* Enable target mode */ + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4); + + /* Disable ini mode, if requested */ + if (!qla_ini_mode_enabled(vha)) + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_5); + + if (ha->enable_explicit_conf) { + ql_log(ql_log_info, vha, 0x1073, "Enabling FC tape support\n"); + /* Disable Full Login after LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + /* Enable FC tapes support */ + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); + } else { + ql_log(ql_log_info, vha, 0x1073, "Disabling FC tape support\n"); + /* Enable Full Login after LIP */ + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_13); + /* Disable FC tapes support */ + nv->firmware_options_2 &= __constant_cpu_to_le32(~BIT_12); + } + /* Enable initial LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9); + } else { + if (ha->saved_set) { + nv->exchange_count = ha->saved_exchange_count; + nv->firmware_options_1 = ha->saved_firmware_options_1; + nv->firmware_options_2 = ha->saved_firmware_options_2; + nv->firmware_options_3 = ha->saved_firmware_options_3; + } + } + /* out-of-order frames reassembly */ + nv->firmware_options_3 |= __constant_cpu_to_le32(BIT_6|BIT_9); + +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + + if (ha->enable_class_2) { + if (vha->flags.init_done) { + fc_host_supported_classes(vha->host) = + FC_COS_CLASS2 | FC_COS_CLASS3; + } + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8); + } else { + if (vha->flags.init_done) + fc_host_supported_classes(vha->host) = FC_COS_CLASS3; + nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8); + } + + /* Reset Initialization control block */ + memset(icb, 0, ha->init_cb_size); + + /* Copy 1st segment. */ + dptr1 = (uint8_t *)icb; + dptr2 = (uint8_t *)&nv->version; + cnt = (uint8_t *)&icb->response_q_inpointer - (uint8_t *)&icb->version; + while (cnt--) + *dptr1++ = *dptr2++; + + icb->login_retry_count = nv->login_retry_count; + + /* Copy 2nd segment. */ + dptr1 = (uint8_t *)&icb->interrupt_delay_timer; + dptr2 = (uint8_t *)&nv->interrupt_delay_timer; + cnt = (uint8_t *)&icb->reserved_5 - + (uint8_t *)&icb->interrupt_delay_timer; + while (cnt--) + *dptr1++ = *dptr2++; + + memcpy(icb->enode_mac, nv->enode_mac, sizeof(icb->enode_mac)); + /* Some boards (with valid NVRAMs) still have NULL enode_mac!! */ + if (!memcmp(icb->enode_mac, "\0\0\0\0\0\0", sizeof(icb->enode_mac))) { + icb->enode_mac[0] = 0x00; + icb->enode_mac[1] = 0xC0; + icb->enode_mac[2] = 0xDD; + icb->enode_mac[3] = 0x04; + icb->enode_mac[4] = 0x05; + icb->enode_mac[5] = 0x06 + ha->port_no; + } + + /* Use extended-initialization control block. */ + memcpy(ha->ex_init_cb, &nv->ex_version, sizeof(*ha->ex_init_cb)); + + /* + * Setup driver NVRAM options. + */ + qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name), + "QLE8XXX"); + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (vha->node_name_set || vha->port_name_set) { + if (vha->node_name_set) + memcpy(icb->node_name, vha->node_name, WWN_SIZE); + if (vha->port_name_set) + memcpy(icb->port_name, vha->port_name, WWN_SIZE); + icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14); + } else +#endif + if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { + /* Use alternate WWN? */ + memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); + memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); + } + + /* Prepare nodename */ + if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) { + /* + * Firmware will apply the following mask if the nodename was + * not provided. + */ + memcpy(icb->node_name, icb->port_name, WWN_SIZE); + icb->node_name[0] &= 0xF0; + } + + /* Set host adapter parameters. */ + ha->flags.disable_risc_code_load = 0; + ha->flags.enable_lip_reset = 0; + ha->flags.enable_lip_full_login = + le32_to_cpu(nv->host_p) & BIT_10 ? 1: 0; + ha->flags.enable_target_reset = + le32_to_cpu(nv->host_p) & BIT_11 ? 1: 0; + ha->flags.enable_led_scheme = 0; + ha->flags.disable_serdes = le32_to_cpu(nv->host_p) & BIT_5 ? 1: 0; + + ha->operating_mode = (le32_to_cpu(icb->firmware_options_2) & + (BIT_6 | BIT_5 | BIT_4)) >> 4; + + /* save HBA serial number */ + ha->serial0 = icb->port_name[5]; + ha->serial1 = icb->port_name[6]; + ha->serial2 = icb->port_name[7]; + memcpy(vha->node_name, icb->node_name, WWN_SIZE); + memcpy(vha->port_name, icb->port_name, WWN_SIZE); + + icb->execution_throttle = __constant_cpu_to_le16(0xFFFF); + + ha->retry_count = le16_to_cpu(nv->login_retry_count); + + /* Set minimum login_timeout to 4 seconds. */ + if (le16_to_cpu(nv->login_timeout) < ql2xlogintimeout) + nv->login_timeout = cpu_to_le16(ql2xlogintimeout); + if (le16_to_cpu(nv->login_timeout) < 4) + nv->login_timeout = __constant_cpu_to_le16(4); + ha->login_timeout = le16_to_cpu(nv->login_timeout); + icb->login_timeout = nv->login_timeout; + + /* Set minimum RATOV to 100 tenths of a second. */ + ha->r_a_tov = 100; + + ha->loop_reset_delay = nv->reset_delay; + + /* Link Down Timeout = 0: + * + * When Port Down timer expires we will start returning + * I/O's to OS with "DID_NO_CONNECT". + * + * Link Down Timeout != 0: + * + * The driver waits for the link to come up after link down + * before returning I/Os to OS with "DID_NO_CONNECT". + */ + if (le16_to_cpu(nv->link_down_timeout) == 0) { + ha->loop_down_abort_time = + (LOOP_DOWN_TIME - LOOP_DOWN_TIMEOUT); + } else { + ha->link_down_timeout = le16_to_cpu(nv->link_down_timeout); + ha->loop_down_abort_time = + (LOOP_DOWN_TIME - ha->link_down_timeout); + } + + /* Need enough time to try and get the port back. */ + ha->port_down_retry_count = le16_to_cpu(nv->port_down_retry_count); + if (qlport_down_retry) + ha->port_down_retry_count = qlport_down_retry; + + /* Set login_retry_count */ + ha->login_retry_count = le16_to_cpu(nv->login_retry_count); + if (ha->port_down_retry_count == + le16_to_cpu(nv->port_down_retry_count) && + ha->port_down_retry_count > 3) + ha->login_retry_count = ha->port_down_retry_count; + else if (ha->port_down_retry_count > (int)ha->login_retry_count) + ha->login_retry_count = ha->port_down_retry_count; + if (ql2xloginretrycount) + ha->login_retry_count = ql2xloginretrycount; + + /* if not running MSI-X we need handshaking on interrupts */ + if (!vha->hw->flags.msix_enabled && IS_QLA83XX(ha)) + icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22); + + /* Enable ZIO. */ + if (!vha->flags.init_done) { +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->zio_mode = QLA_ZIO_DISABLED; +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ + ha->zio_mode = le32_to_cpu(icb->firmware_options_2) & + (BIT_3 | BIT_2 | BIT_1 | BIT_0); + ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ? + le16_to_cpu(icb->interrupt_delay_timer): 2; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + } + icb->firmware_options_2 &= __constant_cpu_to_le32( + ~(BIT_3 | BIT_2 | BIT_1 | BIT_0)); + vha->flags.process_response_queue = 0; + if (ha->zio_mode != QLA_ZIO_DISABLED) { + ha->zio_mode = QLA_ZIO_MODE_6; + + ql_log(ql_log_info, vha, 0x0075, + "ZIO mode %d enabled; timer delay (%d us).\n", + ha->zio_mode, + ha->zio_timer * 100); + + icb->firmware_options_2 |= cpu_to_le32( + (uint32_t)ha->zio_mode); + icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer); + vha->flags.process_response_queue = 1; + } + + if (rval) { + ql_log(ql_log_warn, vha, 0x0076, + "NVRAM configuration failed.\n"); + } + return (rval); +} + +int +qla82xx_restart_isp(scsi_qla_host_t *vha) +{ + int status, rval; + uint32_t wait_time; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; + struct scsi_qla_host *vp; + unsigned long flags; + + status = qla2x00_init_rings(vha); + if (!status) { + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); + ha->flags.chip_reset_done = 1; + + status = qla2x00_fw_ready(vha); + if (!status) { + ql_log(ql_log_info, vha, 0x803c, + "Start configure loop, status =%d.\n", status); + + /* Issue a marker after FW becomes ready. */ + qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); + + vha->flags.online = 1; + /* Wait at most MAX_TARGET RSCNs for a stable link. */ + wait_time = 256; + do { + clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + qla2x00_configure_loop(vha); + wait_time--; + } while (!atomic_read(&vha->loop_down_timer) && + !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) && + wait_time && + (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))); + } + + /* if no cable then assume it's good */ + if ((vha->device_flags & DFLG_NO_CABLE)) + status = 0; + + ql_log(ql_log_info, vha, 0x8000, + "Configure loop done, status = 0x%x.\n", status); + } + + if (!status) { + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); + + if (!atomic_read(&vha->loop_down_timer)) { + /* + * Issue marker command only when we are going + * to start the I/O . + */ + vha->marker_needed = 1; + } + + vha->flags.online = 1; + + ha->isp_ops->enable_intrs(ha); + + ha->isp_abort_cnt = 0; + clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + + /* Update the firmware version */ + status = qla82xx_check_md_needed(vha); + + if (ha->fce) { + ha->flags.fce_enabled = 1; + memset(ha->fce, 0, + fce_calc_size(ha->fce_bufs)); + rval = qla2x00_enable_fce_trace(vha, + ha->fce_dma, ha->fce_bufs, ha->fce_mb, + &ha->fce_bufs); + if (rval) { + ql_log(ql_log_warn, vha, 0x8001, + "Unable to reinitialize FCE (%d).\n", + rval); + ha->flags.fce_enabled = 0; + } + } + + if (ha->eft) { + memset(ha->eft, 0, EFT_SIZE); + rval = qla2x00_enable_eft_trace(vha, + ha->eft_dma, EFT_NUM_BUFFERS); + if (rval) { + ql_log(ql_log_warn, vha, 0x8010, + "Unable to reinitialize EFT (%d).\n", + rval); + } + } + } + + if (!status) { + ql_dbg(ql_dbg_taskm, vha, 0x8011, + "qla82xx_restart_isp succeeded.\n"); + + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + if (vp->vp_idx) { + atomic_inc(&vp->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); + + qla2x00_vp_abort_isp(vp); + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vp->vref_count); + } + } + spin_unlock_irqrestore(&ha->vport_slock, flags); + + } else { + ql_log(ql_log_warn, vha, 0x8016, + "qla82xx_restart_isp **** FAILED ****.\n"); + } + + return status; +} + +void +qla81xx_update_fw_options(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (!ql2xetsenable) + return; + + /* Enable ETS Burst. */ + memset(ha->fw_options, 0, sizeof(ha->fw_options)); + ha->fw_options[2] |= BIT_9; + qla2x00_set_fw_options(vha, ha->fw_options); +} + +/* + * qla24xx_get_fcp_prio + * Gets the fcp cmd priority value for the logged in port. + * Looks for a match of the port descriptors within + * each of the fcp prio config entries. If a match is found, + * the tag (priority) value is returned. + * + * Input: + * vha = scsi host structure pointer. + * fcport = port structure pointer. + * + * Return: + * non-zero (if found) + * -1 (if not found) + * + * Context: + * Kernel context + */ +static int +qla24xx_get_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport) +{ + int i, entries; + uint8_t pid_match, wwn_match; + int priority; + uint32_t pid1, pid2; + uint64_t wwn1, wwn2; + struct qla_fcp_prio_entry *pri_entry; + struct qla_hw_data *ha = vha->hw; + + if (!ha->fcp_prio_cfg || !ha->flags.fcp_prio_enabled) + return -1; + + priority = -1; + entries = ha->fcp_prio_cfg->num_entries; + pri_entry = &ha->fcp_prio_cfg->entry[0]; + + for (i = 0; i < entries; i++) { + pid_match = wwn_match = 0; + + if (!(pri_entry->flags & FCP_PRIO_ENTRY_VALID)) { + pri_entry++; + continue; + } + + /* check source pid for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_SPID_VALID) { + pid1 = pri_entry->src_pid & INVALID_PORT_ID; + pid2 = vha->d_id.b24 & INVALID_PORT_ID; + if (pid1 == INVALID_PORT_ID) + pid_match++; + else if (pid1 == pid2) + pid_match++; + } + + /* check destination pid for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_DPID_VALID) { + pid1 = pri_entry->dst_pid & INVALID_PORT_ID; + pid2 = fcport->d_id.b24 & INVALID_PORT_ID; + if (pid1 == INVALID_PORT_ID) + pid_match++; + else if (pid1 == pid2) + pid_match++; + } + + /* check source WWN for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_SWWN_VALID) { + wwn1 = wwn_to_u64(vha->port_name); + wwn2 = wwn_to_u64(pri_entry->src_wwpn); + if (wwn2 == (uint64_t)-1) + wwn_match++; + else if (wwn1 == wwn2) + wwn_match++; + } + + /* check destination WWN for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_DWWN_VALID) { + wwn1 = wwn_to_u64(fcport->port_name); + wwn2 = wwn_to_u64(pri_entry->dst_wwpn); + if (wwn2 == (uint64_t)-1) + wwn_match++; + else if (wwn1 == wwn2) + wwn_match++; + } + + if (pid_match == 2 || wwn_match == 2) { + /* Found a matching entry */ + if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID) + priority = pri_entry->tag; + break; + } + + pri_entry++; + } + + return priority; +} + +/* + * qla24xx_update_fcport_fcp_prio + * Activates fcp priority for the logged in fc port + * + * Input: + * vha = scsi host structure pointer. + * fcp = port structure pointer. + * + * Return: + * QLA_SUCCESS or QLA_FUNCTION_FAILED + * + * Context: + * Kernel context. + */ +int +qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport) +{ + int ret; + int priority; + uint16_t mb[5]; + + if (fcport->port_type != FCT_TARGET || + fcport->loop_id == FC_NO_LOOP_ID) + return QLA_FUNCTION_FAILED; + + priority = qla24xx_get_fcp_prio(vha, fcport); + if (priority < 0) + return QLA_FUNCTION_FAILED; + + if (IS_QLA82XX(vha->hw)) { + fcport->fcp_prio = priority & 0xf; + return QLA_SUCCESS; + } + + ret = qla24xx_set_fcp_prio(vha, fcport->loop_id, priority, mb); + if (ret == QLA_SUCCESS) { + if (fcport->fcp_prio != priority) + ql_dbg(ql_dbg_user, vha, 0x709e, + "Updated FCP_CMND priority - value=%d loop_id=%d " + "port_id=%02x%02x%02x.\n", priority, + fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); + fcport->fcp_prio = priority & 0xf; + } else + ql_dbg(ql_dbg_user, vha, 0x704f, + "Unable to update FCP_CMND priority - ret=0x%x for " + "loop_id=%d port_id=%02x%02x%02x.\n", ret, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa); + + return ret; +} + +/* + * qla24xx_update_all_fcp_prio + * Activates fcp priority for all the logged in ports + * + * Input: + * ha = adapter block pointer. + * + * Return: + * QLA_SUCCESS or QLA_FUNCTION_FAILED + * + * Context: + * Kernel context. + */ +int +qla24xx_update_all_fcp_prio(scsi_qla_host_t *vha) +{ + int ret; + fc_port_t *fcport; + + ret = QLA_FUNCTION_FAILED; + /* We need to set priority for all logged in ports */ + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) + ret = qla24xx_update_fcport_fcp_prio(vha, fcport); + + return ret; +} diff --git a/qla2x00t/qla_inline.h b/qla2x00t/qla_inline.h index 9b5e618ef..1f9c75860 100644 --- a/qla2x00t/qla_inline.h +++ b/qla2x00t/qla_inline.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -32,17 +32,20 @@ qla2x00_debounce_register(volatile uint16_t __iomem *addr) } static inline void -qla2x00_poll(scsi_qla_host_t *ha) +qla2x00_poll(struct rsp_que *rsp) { unsigned long flags; - + struct qla_hw_data *ha = rsp->hw; #ifdef CONFIG_PREEMPT_RT_FULL local_irq_save_nort(flags); #else local_irq_save(flags); #endif - ha->isp_ops->intr_handler(0, ha); + if (IS_QLA82XX(ha)) + qla82xx_poll(0, rsp); + else + ha->isp_ops->intr_handler(0, rsp); #ifdef CONFIG_PREEMPT_RT_FULL local_irq_restore_nort(flags); @@ -51,12 +54,6 @@ qla2x00_poll(scsi_qla_host_t *ha) #endif } -static __inline__ scsi_qla_host_t * -to_qla_parent(scsi_qla_host_t *ha) -{ - return ha->parent ? ha->parent : ha; -} - static inline uint8_t * host_to_fcp_swap(uint8_t *fcp, uint32_t bsize) { @@ -71,11 +68,132 @@ host_to_fcp_swap(uint8_t *fcp, uint32_t bsize) } static inline int -qla2x00_is_reserved_id(scsi_qla_host_t *ha, uint16_t loop_id) +qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id) { + struct qla_hw_data *ha = vha->hw; if (IS_FWI2_CAPABLE(ha)) return (loop_id > NPH_LAST_HANDLE); - return ((loop_id > ha->last_loop_id && loop_id < SNS_FIRST_LOOP_ID) || + return ((loop_id > ha->max_loop_id && loop_id < SNS_FIRST_LOOP_ID) || loop_id == MANAGEMENT_SERVER || loop_id == BROADCAST); -}; +} + +static inline void +qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp) +{ + struct dsd_dma *dsd_ptr, *tdsd_ptr; + struct crc_context *ctx; + + ctx = (struct crc_context *)GET_CMD_CTX_SP(sp); + + /* clean up allocated prev pool */ + list_for_each_entry_safe(dsd_ptr, tdsd_ptr, + &ctx->dsd_list, list) { + dma_pool_free(ha->dl_dma_pool, dsd_ptr->dsd_addr, + dsd_ptr->dsd_list_dma); + list_del(&dsd_ptr->list); + kfree(dsd_ptr); + } + INIT_LIST_HEAD(&ctx->dsd_list); +} + +static inline void +qla2x00_set_fcport_state(fc_port_t *fcport, int state) +{ + int old_state; + + old_state = atomic_read(&fcport->state); + atomic_set(&fcport->state, state); + + /* Don't print state transitions during initial allocation of fcport */ + if (old_state && old_state != state) { + ql_dbg(ql_dbg_disc, fcport->vha, 0x207d, + "FCPort state transitioned from %s to %s - " + "portid=%02x%02x%02x.\n", + port_state_str[old_state], port_state_str[state], + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa); + } +} + +static inline int +qla2x00_hba_err_chk_enabled(srb_t *sp) +{ + /* + * Uncomment when corresponding SCSI changes are done. + * + if (!sp->cmd->prot_chk) + return 0; + * + */ + switch (scsi_get_prot_op(GET_CMD_SP(sp))) { + case SCSI_PROT_READ_STRIP: + case SCSI_PROT_WRITE_INSERT: + if (ql2xenablehba_err_chk >= 1) + return 1; + break; + case SCSI_PROT_READ_PASS: + case SCSI_PROT_WRITE_PASS: + if (ql2xenablehba_err_chk >= 2) + return 1; + break; + case SCSI_PROT_READ_INSERT: + case SCSI_PROT_WRITE_STRIP: + return 1; + } + return 0; +} + +static inline srb_t * +qla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag) +{ + srb_t *sp = NULL; + struct qla_hw_data *ha = vha->hw; + uint8_t bail; + + QLA_VHA_MARK_BUSY(vha, bail); + if (unlikely(bail)) + return NULL; + + sp = mempool_alloc(ha->srb_mempool, flag); + if (!sp) + goto done; + + memset(sp, 0, sizeof(*sp)); + sp->fcport = fcport; + sp->iocbs = 1; +done: + if (!sp) + QLA_VHA_MARK_NOT_BUSY(vha); + return sp; +} + +static inline void +qla2x00_init_timer(srb_t *sp, unsigned long tmo) +{ + init_timer(&sp->u.iocb_cmd.timer); + sp->u.iocb_cmd.timer.expires = jiffies + tmo * HZ; + sp->u.iocb_cmd.timer.data = (unsigned long)sp; + sp->u.iocb_cmd.timer.function = qla2x00_sp_timeout; + add_timer(&sp->u.iocb_cmd.timer); + sp->free = qla2x00_sp_free; +} + +static inline int +qla2x00_reset_active(scsi_qla_host_t *vha) +{ + scsi_qla_host_t *base_vha = pci_get_drvdata(vha->hw->pdev); + + /* Test appropriate base-vha and vha flags. */ + return test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) || + test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags); +} + +static inline int +qla2x00_gid_list_size(struct qla_hw_data *ha) +{ + return (sizeof(struct gid_list_info) * ha->max_fibre_devices); +} diff --git a/qla2x00t/qla_iocb.c b/qla2x00t/qla_iocb.c index 1d9ea02af..58465ee07 100644 --- a/qla2x00t/qla_iocb.c +++ b/qla2x00t/qla_iocb.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -12,7 +12,7 @@ #include #include "qla2x_tgt.h" - +static void qla25xx_set_que(srb_t *, struct rsp_que **); /** * qla2x00_get_cmd_direction() - Determine control_flag data direction. * @cmd: SCSI command @@ -20,17 +20,22 @@ * Returns the proper CF_* direction based on CDB. */ static inline uint16_t -qla2x00_get_cmd_direction(struct scsi_cmnd *cmd) +qla2x00_get_cmd_direction(srb_t *sp) { uint16_t cflags; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + struct scsi_qla_host *vha = sp->fcport->vha; cflags = 0; /* Set transfer direction */ - if (cmd->sc_data_direction == DMA_TO_DEVICE) + if (cmd->sc_data_direction == DMA_TO_DEVICE) { cflags = CF_WRITE; - else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + vha->qla_stats.output_bytes += scsi_bufflen(cmd); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { cflags = CF_READ; + vha->qla_stats.input_bytes += scsi_bufflen(cmd); + } return (cflags); } @@ -85,20 +90,20 @@ qla2x00_calc_iocbs_64(uint16_t dsds) * Returns a pointer to the Continuation Type 0 IOCB packet. */ static inline cont_entry_t * -qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha) +qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha) { cont_entry_t *cont_pkt; - + struct req_que *req = vha->req; /* Adjust ring index. */ - ha->req_ring_index++; - if (ha->req_ring_index == ha->request_q_length) { - ha->req_ring_index = 0; - ha->request_ring_ptr = ha->request_ring; + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; } else { - ha->request_ring_ptr++; + req->ring_ptr++; } - cont_pkt = (cont_entry_t *)ha->request_ring_ptr; + cont_pkt = (cont_entry_t *)req->ring_ptr; /* Load packet defaults. */ *((uint32_t *)(&cont_pkt->entry_type)) = @@ -114,20 +119,20 @@ qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha) * Returns a pointer to the continuation type 1 IOCB packet. */ static inline cont_a64_entry_t * -qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *ha) +qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req) { cont_a64_entry_t *cont_pkt; /* Adjust ring index. */ - ha->req_ring_index++; - if (ha->req_ring_index == ha->request_q_length) { - ha->req_ring_index = 0; - ha->request_ring_ptr = ha->request_ring; + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; } else { - ha->request_ring_ptr++; + req->ring_ptr++; } - cont_pkt = (cont_a64_entry_t *)ha->request_ring_ptr; + cont_pkt = (cont_a64_entry_t *)req->ring_ptr; /* Load packet defaults. */ *((uint32_t *)(&cont_pkt->entry_type)) = @@ -136,7 +141,51 @@ qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *ha) return (cont_pkt); } -/** +static inline int +qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts) +{ + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + uint8_t guard = scsi_host_get_guard(cmd->device->host); + + /* We only support T10 DIF right now */ + if (guard != SHOST_DIX_GUARD_CRC) { + ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3007, + "Unsupported guard: %d for cmd=%p.\n", guard, cmd); + return 0; + } + + /* We always use DIFF Bundling for best performance */ + *fw_prot_opts = 0; + + /* Translate SCSI opcode to a protection opcode */ + switch (scsi_get_prot_op(cmd)) { + case SCSI_PROT_READ_STRIP: + *fw_prot_opts |= PO_MODE_DIF_REMOVE; + break; + case SCSI_PROT_WRITE_INSERT: + *fw_prot_opts |= PO_MODE_DIF_INSERT; + break; + case SCSI_PROT_READ_INSERT: + *fw_prot_opts |= PO_MODE_DIF_INSERT; + break; + case SCSI_PROT_WRITE_STRIP: + *fw_prot_opts |= PO_MODE_DIF_REMOVE; + break; + case SCSI_PROT_READ_PASS: + *fw_prot_opts |= PO_MODE_DIF_PASS; + break; + case SCSI_PROT_WRITE_PASS: + *fw_prot_opts |= PO_MODE_DIF_PASS; + break; + default: /* Normal Request */ + *fw_prot_opts |= PO_MODE_DIF_PASS; + break; + } + + return scsi_prot_sg_count(cmd); +} + +/* * qla2x00_build_scsi_iocbs_32() - Build IOCB command utilizing 32bit * capable IOCB types. * @@ -149,12 +198,12 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, { uint16_t avail_dsds; uint32_t *cur_dsd; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; struct scsi_cmnd *cmd; struct scatterlist *sg; int i; - cmd = sp->cmd; + cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 2 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = @@ -166,9 +215,8 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, return; } - ha = sp->ha; - - cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd)); + vha = sp->fcport->vha; + cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); /* Three DSDs are available in the Command Type 2 IOCB */ avail_dsds = 3; @@ -184,7 +232,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, * Seven DSDs are available in the Continuation * Type 0 IOCB. */ - cont_pkt = qla2x00_prep_cont_type0_iocb(ha); + cont_pkt = qla2x00_prep_cont_type0_iocb(vha); cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address; avail_dsds = 7; } @@ -208,12 +256,12 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, { uint16_t avail_dsds; uint32_t *cur_dsd; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; struct scsi_cmnd *cmd; struct scatterlist *sg; int i; - cmd = sp->cmd; + cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 3 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = @@ -225,9 +273,8 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, return; } - ha = sp->ha; - - cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd)); + vha = sp->fcport->vha; + cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); /* Two DSDs are available in the Command Type 3 IOCB */ avail_dsds = 2; @@ -244,7 +291,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, * Five DSDs are available in the Continuation * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(ha); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); cur_dsd = (uint32_t *)cont_pkt->dseg_0_address; avail_dsds = 5; } @@ -268,7 +315,7 @@ qla2x00_start_scsi(srb_t *sp) { int ret, nseg; unsigned long flags; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *vha; struct scsi_cmnd *cmd; uint32_t *clr_ptr; uint32_t index; @@ -278,34 +325,43 @@ qla2x00_start_scsi(srb_t *sp) uint16_t req_cnt; uint16_t tot_dsds; struct device_reg_2xxx __iomem *reg; + struct qla_hw_data *ha; + struct req_que *req; + struct rsp_que *rsp; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + char tag[2]; +#endif /* Setup device pointers. */ ret = 0; - ha = sp->ha; - pha = to_qla_parent(ha); + vha = sp->fcport->vha; + ha = vha->hw; reg = &ha->iobase->isp; - cmd = sp->cmd; + cmd = GET_CMD_SP(sp); + req = ha->req_q_map[0]; + rsp = ha->rsp_q_map[0]; /* So we know we haven't pci_map'ed anything yet */ tot_dsds = 0; /* Send marker if required */ - if (ha->marker_needed != 0) { - if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) { + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) { return (QLA_FUNCTION_FAILED); } - ha->marker_needed = 0; + vha->marker_needed = 0; } /* Acquire ring specific lock */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); /* Check for room in outstanding command list. */ - handle = ha->current_outstanding_cmd; + handle = req->current_outstanding_cmd; for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { handle++; if (handle == MAX_OUTSTANDING_COMMANDS) handle = 1; - if (!ha->outstanding_cmds[handle]) + if (!req->outstanding_cmds[handle]) break; } if (index == MAX_OUTSTANDING_COMMANDS) @@ -324,25 +380,26 @@ qla2x00_start_scsi(srb_t *sp) /* Calculate the number of request entries needed. */ req_cnt = ha->isp_ops->calc_req_entries(tot_dsds); - if (ha->req_q_cnt < (req_cnt + 2)) { + if (req->cnt < (req_cnt + 2)) { cnt = RD_REG_WORD_RELAXED(ISP_REQ_Q_OUT(ha, reg)); - if (ha->req_ring_index < cnt) - ha->req_q_cnt = cnt - ha->req_ring_index; + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; else - ha->req_q_cnt = ha->request_q_length - - (ha->req_ring_index - cnt); + req->cnt = req->length - + (req->ring_index - cnt); } - if (ha->req_q_cnt < (req_cnt + 2)) - goto queuing_error; + /* If still no head room then bail out */ + if (req->cnt < (req_cnt + 2)) + goto queuing_error; /* Build command packet */ - ha->current_outstanding_cmd = handle; - ha->outstanding_cmds[handle] = sp; - sp->ha = ha; - sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; - ha->req_q_cnt -= req_cnt; + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; + req->cnt -= req_cnt; - cmd_pkt = (cmd_entry_t *)ha->request_ring_ptr; + cmd_pkt = (cmd_entry_t *)req->ring_ptr; cmd_pkt->handle = handle; /* Zero out remaining portion of packet. */ clr_ptr = (uint32_t *)cmd_pkt + 2; @@ -351,10 +408,30 @@ qla2x00_start_scsi(srb_t *sp) /* Set target ID and LUN number*/ SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id); - cmd_pkt->lun = cpu_to_le16(sp->cmd->device->lun); + cmd_pkt->lun = cpu_to_le16(cmd->device->lun); /* Update tagged queuing modifier */ - cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + if (scsi_populate_tag_msg(cmd, tag)) { + switch (tag[0]) { + case HEAD_OF_QUEUE_TAG: + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_HEAD_TAG); + break; + case ORDERED_QUEUE_TAG: + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_ORDERED_TAG); + break; + default: + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_SIMPLE_TAG); + break; + } + } +#else + if (cmd->flags & SCMD_TAGGED) + cmd_pkt->control_flags = cpu_to_le16(CF_SIMPLE_TAG); +#endif /* Load SCSI command packet. */ memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len); @@ -368,36 +445,72 @@ qla2x00_start_scsi(srb_t *sp) wmb(); /* Adjust ring index. */ - ha->req_ring_index++; - if (ha->req_ring_index == ha->request_q_length) { - ha->req_ring_index = 0; - ha->request_ring_ptr = ha->request_ring; + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; } else - ha->request_ring_ptr++; + req->ring_ptr++; sp->flags |= SRB_DMA_VALID; /* Set chip new ring index. */ - WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index); + WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), req->ring_index); RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */ /* Manage unprocessed RIO/ZIO commands in response queue. */ - if (ha->flags.process_response_queue && - ha->response_ring_ptr->signature != RESPONSE_PROCESSED) - qla2x00_process_response_queue(ha); + if (vha->flags.process_response_queue && + rsp->ring_ptr->signature != RESPONSE_PROCESSED) + qla2x00_process_response_queue(rsp); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return (QLA_SUCCESS); queuing_error: if (tot_dsds) scsi_dma_unmap(cmd); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return (QLA_FUNCTION_FAILED); } +/** + * qla2x00_start_iocbs() - Execute the IOCB command + */ +void +qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) +{ + struct qla_hw_data *ha = vha->hw; + device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); + + if (IS_QLA82XX(ha)) { + qla82xx_start_iocbs(vha); + } else { + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + /* Set chip new ring index. */ + if (ha->mqenable || IS_QLA83XX(ha)) { + WRT_REG_DWORD(req->req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr); + } else if (IS_FWI2_CAPABLE(ha)) { + WRT_REG_DWORD(®->isp24.req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(®->isp24.req_q_in); + } else { + WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), + req->ring_index); + RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); + } + } +} +EXPORT_SYMBOL(qla2x00_start_iocbs); + /** * qla2x00_marker() - Send a marker IOCB to the firmware. * @ha: HA context @@ -412,18 +525,21 @@ queuing_error: * Hardware lock must be held on entrance. Might release it, then reacquire. */ int -__qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, - uint8_t type) +__qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, + struct rsp_que *rsp, uint16_t loop_id, + uint16_t lun, uint8_t type) { mrk_entry_t *mrk; struct mrk_entry_24xx *mrk24; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); mrk24 = NULL; - mrk = (mrk_entry_t *)qla2x00_req_pkt(pha); + req = ha->req_q_map[0]; + mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, 0); if (mrk == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n", - __func__, ha->host_no)); + ql_log(ql_log_warn, base_vha, 0x3026, + "Failed to allocate Marker IOCB.\n"); return (QLA_FUNCTION_FAILED); } @@ -437,7 +553,8 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, mrk24->lun[1] = LSB(lun); mrk24->lun[2] = MSB(lun); host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun)); - mrk24->vp_index = ha->vp_idx; + mrk24->vp_index = vha->vp_idx; + mrk24->handle = MAKE_HANDLE(req->id, mrk24->handle); } else { SET_TARGET_ID(ha, mrk->target, loop_id); mrk->lun = cpu_to_le16(lun); @@ -445,22 +562,22 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, } wmb(); - qla2x00_isp_cmd(pha); + qla2x00_start_iocbs(vha, req); return (QLA_SUCCESS); } int -qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, - uint8_t type) +qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, + struct rsp_que *rsp, uint16_t loop_id, uint16_t lun, + uint8_t type) { int ret; - unsigned long flags; - scsi_qla_host_t *pha = to_qla_parent(ha); + unsigned long flags = 0; - spin_lock_irqsave(&pha->hardware_lock, flags); - ret = __qla2x00_marker(ha, loop_id, lun, type); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_lock_irqsave(&vha->hw->hardware_lock, flags); + ret = __qla2x00_marker(vha, req, rsp, loop_id, lun, type); + spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); return (ret); } @@ -475,36 +592,35 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, * Returns NULL if function failed, else, a pointer to the request packet. */ request_t * -qla2x00_req_pkt(scsi_qla_host_t *ha) +qla2x00_req_pkt(scsi_qla_host_t *vha) { + struct qla_hw_data *ha = vha->hw; device_reg_t __iomem *reg = ha->iobase; - scsi_qla_host_t *pha = to_qla_parent(ha); - request_t *pkt = NULL; - uint16_t cnt; - uint32_t *dword_ptr; - uint32_t timer; - uint16_t req_cnt = 1; + request_t *pkt = NULL; + uint32_t *dword_ptr, timer; + uint16_t req_cnt = 1, cnt; /* Wait 1 second for slot. */ for (timer = HZ; timer; timer--) { - if ((req_cnt + 2) >= ha->req_q_cnt) { + if ((req_cnt + 2) >= vha->req->cnt) { /* Calculate number of free request entries. */ if (IS_FWI2_CAPABLE(ha)) - cnt = (uint16_t)RD_REG_DWORD( - ®->isp24.req_q_out); + cnt = (uint16_t)RD_REG_DWORD(®->isp24.req_q_out); else cnt = qla2x00_debounce_register( - ISP_REQ_Q_OUT(ha, ®->isp)); - if (ha->req_ring_index < cnt) - ha->req_q_cnt = cnt - ha->req_ring_index; + ISP_REQ_Q_OUT(ha, ®->isp)); + + if (vha->req->ring_index < cnt) + vha->req->cnt = cnt - vha->req->ring_index; else - ha->req_q_cnt = ha->request_q_length - - (ha->req_ring_index - cnt); + vha->req->cnt = vha->req->length - + (vha->req->ring_index - cnt); } + /* If room for request in request ring. */ - if ((req_cnt + 2) < ha->req_q_cnt) { - ha->req_q_cnt--; - pkt = ha->request_ring_ptr; + if ((req_cnt + 2) < vha->req->cnt) { + vha->req->cnt--; + pkt = vha->req->ring_ptr; /* Zero out packet. */ dword_ptr = (uint32_t *)pkt; @@ -512,68 +628,36 @@ qla2x00_req_pkt(scsi_qla_host_t *ha) *dword_ptr++ = 0; /* Set system defined field. */ - pkt->sys_define = (uint8_t)ha->req_ring_index; + pkt->sys_define = (uint8_t)vha->req->ring_index; /* Set entry count. */ pkt->entry_count = 1; - break; + return pkt; } /* Release ring specific lock */ - spin_unlock(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); - udelay(2); /* 2 us */ + /* 2 us */ + udelay(2); + /* + * Check for pending interrupts, during init we issue marker directly + */ + if (!vha->marker_needed && !vha->flags.init_done) + qla2x00_poll(vha->req->rsp); - /* Check for pending interrupts. */ - /* During init we issue marker directly */ - if (!ha->marker_needed && !ha->flags.init_done) - qla2x00_poll(ha); - - spin_lock_irq(&pha->hardware_lock); - } - if (!pkt) { - DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__)); + /* Reaquire ring specific lock */ + spin_lock_irq(&ha->hardware_lock); } - return (pkt); + printk(KERN_INFO "Unable to locate request_t *pkt in ring\n"); + dump_stack(); + + return NULL; } EXPORT_SYMBOL(qla2x00_req_pkt); -/** - * qla2x00_isp_cmd() - Modify the request ring pointer. - * @ha: HA context - * - * Note: The caller must hold the hardware lock before calling this routine. - */ -void -qla2x00_isp_cmd(scsi_qla_host_t *ha) -{ - device_reg_t __iomem *reg = ha->iobase; - - DEBUG5(printk("%s(): IOCB data:\n", __func__)); - DEBUG5(qla2x00_dump_buffer( - (uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE)); - - /* Adjust ring index. */ - ha->req_ring_index++; - if (ha->req_ring_index == ha->request_q_length) { - ha->req_ring_index = 0; - ha->request_ring_ptr = ha->request_ring; - } else - ha->request_ring_ptr++; - - /* Set chip new ring index. */ - if (IS_FWI2_CAPABLE(ha)) { - WRT_REG_DWORD(®->isp24.req_q_in, ha->req_ring_index); - RD_REG_DWORD_RELAXED(®->isp24.req_q_in); - } else { - WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), ha->req_ring_index); - RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); - } -} -EXPORT_SYMBOL(qla2x00_isp_cmd); - /** * qla24xx_calc_iocbs() - Determine number of Command Type 3 and * Continuation Type 1 IOCBs to allocate. @@ -582,8 +666,8 @@ EXPORT_SYMBOL(qla2x00_isp_cmd); * * Returns the number of IOCB entries needed to store @dsds. */ -static inline uint16_t -qla24xx_calc_iocbs(uint16_t dsds) +inline uint16_t +qla24xx_calc_iocbs(scsi_qla_host_t *vha, uint16_t dsds) { uint16_t iocbs; @@ -596,6 +680,118 @@ qla24xx_calc_iocbs(uint16_t dsds) return iocbs; } +static inline int +qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, + uint16_t tot_dsds) +{ + uint32_t *cur_dsd = NULL; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct scsi_cmnd *cmd; + struct scatterlist *cur_seg; + uint32_t *dsd_seg; + void *next_dsd; + uint8_t avail_dsds; + uint8_t first_iocb = 1; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct ct6_dsd *ctx; + + cmd = GET_CMD_SP(sp); + + /* Update entry type to indicate Command Type 3 IOCB */ + *((uint32_t *)(&cmd_pkt->entry_type)) = + __constant_cpu_to_le32(COMMAND_TYPE_6); + + /* No data transfer */ + if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { + cmd_pkt->byte_count = __constant_cpu_to_le32(0); + return 0; + } + + vha = sp->fcport->vha; + ha = vha->hw; + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_WRITE_DATA); + vha->qla_stats.output_bytes += scsi_bufflen(cmd); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_READ_DATA); + vha->qla_stats.input_bytes += scsi_bufflen(cmd); + } + + cur_seg = scsi_sglist(cmd); + ctx = GET_CMD_CTX_SP(sp); + + while (tot_dsds) { + avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : tot_dsds; + tot_dsds -= avail_dsds; + dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE; + + dsd_ptr = list_first_entry(&ha->gbl_dsd_list, + struct dsd_dma, list); + next_dsd = dsd_ptr->dsd_addr; + list_del(&dsd_ptr->list); + ha->gbl_dsd_avail--; + list_add_tail(&dsd_ptr->list, &ctx->dsd_list); + ctx->dsd_use_cnt++; + ha->gbl_dsd_inuse++; + + if (first_iocb) { + first_iocb = 0; + dsd_seg = (uint32_t *)&cmd_pkt->fcp_data_dseg_address; + *dsd_seg++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *dsd_seg++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + cmd_pkt->fcp_data_dseg_len = cpu_to_le32(dsd_list_len); + } else { + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(dsd_list_len); + } + cur_dsd = (uint32_t *)next_dsd; + while (avail_dsds) { + dma_addr_t sle_dma; + + sle_dma = sg_dma_address(cur_seg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg)); + cur_seg = sg_next(cur_seg); + avail_dsds--; + } + } + + /* Null termination */ + *cur_dsd++ = 0; + *cur_dsd++ = 0; + *cur_dsd++ = 0; + cmd_pkt->control_flags |= CF_DATA_SEG_DESCR_ENABLE; + return 0; +} + +/* + * qla24xx_calc_dsd_lists() - Determine number of DSD list required + * for Command Type 6. + * + * @dsds: number of data segment decriptors needed + * + * Returns the number of dsd list needed to store @dsds. + */ +inline uint16_t +qla24xx_calc_dsd_lists(uint16_t dsds) +{ + uint16_t dsd_lists = 0; + + dsd_lists = (dsds/QLA_DSDS_PER_IOCB); + if (dsds % QLA_DSDS_PER_IOCB) + dsd_lists++; + return dsd_lists; +} + /** * qla24xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7 * IOCB types. @@ -604,18 +800,19 @@ qla24xx_calc_iocbs(uint16_t dsds) * @cmd_pkt: Command type 3 IOCB * @tot_dsds: Total number of segments to transfer */ -static inline void +inline void qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, uint16_t tot_dsds) { uint16_t avail_dsds; uint32_t *cur_dsd; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; struct scsi_cmnd *cmd; struct scatterlist *sg; int i; + struct req_que *req; - cmd = sp->cmd; + cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 3 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = @@ -627,15 +824,19 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, return; } - ha = sp->ha; + vha = sp->fcport->vha; + req = vha->req; /* Set transfer direction */ - if (cmd->sc_data_direction == DMA_TO_DEVICE) + if (cmd->sc_data_direction == DMA_TO_DEVICE) { cmd_pkt->task_mgmt_flags = __constant_cpu_to_le16(TMF_WRITE_DATA); - else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + vha->qla_stats.output_bytes += scsi_bufflen(cmd); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { cmd_pkt->task_mgmt_flags = __constant_cpu_to_le16(TMF_READ_DATA); + vha->qla_stats.input_bytes += scsi_bufflen(cmd); + } /* One DSD is available in the Command Type 3 IOCB */ avail_dsds = 1; @@ -653,7 +854,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, * Five DSDs are available in the Continuation * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(ha); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); cur_dsd = (uint32_t *)cont_pkt->dseg_0_address; avail_dsds = 5; } @@ -666,6 +867,651 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, } } +struct fw_dif_context { + uint32_t ref_tag; + uint16_t app_tag; + uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/ + uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/ +}; + +/* + * qla24xx_set_t10dif_tags_from_cmd - Extract Ref and App tags from SCSI command + * + */ +static inline void +qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt, + unsigned int protcnt) +{ + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); + + switch (scsi_get_prot_type(cmd)) { + case SCSI_PROT_DIF_TYPE0: + /* + * No check for ql2xenablehba_err_chk, as it would be an + * I/O error if hba tag generation is not done. + */ + pkt->ref_tag = cpu_to_le32((uint32_t) + (0xffffffff & scsi_get_lba(cmd))); + + if (!qla2x00_hba_err_chk_enabled(sp)) + break; + + pkt->ref_tag_mask[0] = 0xff; + pkt->ref_tag_mask[1] = 0xff; + pkt->ref_tag_mask[2] = 0xff; + pkt->ref_tag_mask[3] = 0xff; + break; + + /* + * For TYPE 2 protection: 16 bit GUARD + 32 bit REF tag has to + * match LBA in CDB + N + */ + case SCSI_PROT_DIF_TYPE2: + pkt->app_tag = __constant_cpu_to_le16(0); + pkt->app_tag_mask[0] = 0x0; + pkt->app_tag_mask[1] = 0x0; + + pkt->ref_tag = cpu_to_le32((uint32_t) + (0xffffffff & scsi_get_lba(cmd))); + + if (!qla2x00_hba_err_chk_enabled(sp)) + break; + + /* enable ALL bytes of the ref tag */ + pkt->ref_tag_mask[0] = 0xff; + pkt->ref_tag_mask[1] = 0xff; + pkt->ref_tag_mask[2] = 0xff; + pkt->ref_tag_mask[3] = 0xff; + break; + + /* For Type 3 protection: 16 bit GUARD only */ + case SCSI_PROT_DIF_TYPE3: + pkt->ref_tag_mask[0] = pkt->ref_tag_mask[1] = + pkt->ref_tag_mask[2] = pkt->ref_tag_mask[3] = + 0x00; + break; + + /* + * For TYpe 1 protection: 16 bit GUARD tag, 32 bit REF tag, and + * 16 bit app tag. + */ + case SCSI_PROT_DIF_TYPE1: + pkt->ref_tag = cpu_to_le32((uint32_t) + (0xffffffff & scsi_get_lba(cmd))); + pkt->app_tag = __constant_cpu_to_le16(0); + pkt->app_tag_mask[0] = 0x0; + pkt->app_tag_mask[1] = 0x0; + + if (!qla2x00_hba_err_chk_enabled(sp)) + break; + + /* enable ALL bytes of the ref tag */ + pkt->ref_tag_mask[0] = 0xff; + pkt->ref_tag_mask[1] = 0xff; + pkt->ref_tag_mask[2] = 0xff; + pkt->ref_tag_mask[3] = 0xff; + break; + } + + ql_dbg(ql_dbg_io, vha, 0x3009, + "Setting protection Tags: (BIG) ref tag = 0x%x, app tag = 0x%x, " + "prot SG count %d, cmd lba 0x%x, prot_type=%u cmd=%p.\n", + pkt->ref_tag, pkt->app_tag, protcnt, (int)scsi_get_lba(cmd), + scsi_get_prot_type(cmd), cmd); +} + +struct qla2_sgx { + dma_addr_t dma_addr; /* OUT */ + uint32_t dma_len; /* OUT */ + + uint32_t tot_bytes; /* IN */ + struct scatterlist *cur_sg; /* IN */ + + /* for book keeping, bzero on initial invocation */ + uint32_t bytes_consumed; + uint32_t num_bytes; + uint32_t tot_partial; + + /* for debugging */ + uint32_t num_sg; + srb_t *sp; +}; + +static int +qla24xx_get_one_block_sg(uint32_t blk_sz, struct qla2_sgx *sgx, + uint32_t *partial) +{ + struct scatterlist *sg; + uint32_t cumulative_partial, sg_len; + dma_addr_t sg_dma_addr; + + if (sgx->num_bytes == sgx->tot_bytes) + return 0; + + sg = sgx->cur_sg; + cumulative_partial = sgx->tot_partial; + + sg_dma_addr = sg_dma_address(sg); + sg_len = sg_dma_len(sg); + + sgx->dma_addr = sg_dma_addr + sgx->bytes_consumed; + + if ((cumulative_partial + (sg_len - sgx->bytes_consumed)) >= blk_sz) { + sgx->dma_len = (blk_sz - cumulative_partial); + sgx->tot_partial = 0; + sgx->num_bytes += blk_sz; + *partial = 0; + } else { + sgx->dma_len = sg_len - sgx->bytes_consumed; + sgx->tot_partial += sgx->dma_len; + *partial = 1; + } + + sgx->bytes_consumed += sgx->dma_len; + + if (sg_len == sgx->bytes_consumed) { + sg = sg_next(sg); + sgx->num_sg++; + sgx->cur_sg = sg; + sgx->bytes_consumed = 0; + } + + return 1; +} + +static int +qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp, + uint32_t *dsd, uint16_t tot_dsds) +{ + void *next_dsd; + uint8_t avail_dsds = 0; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct scatterlist *sg_prot; + uint32_t *cur_dsd = dsd; + uint16_t used_dsds = tot_dsds; + + uint32_t prot_int; + uint32_t partial; + struct qla2_sgx sgx; + dma_addr_t sle_dma; + uint32_t sle_dma_len, tot_prot_dma_len = 0; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + + prot_int = cmd->device->sector_size; + + memset(&sgx, 0, sizeof(struct qla2_sgx)); + sgx.tot_bytes = scsi_bufflen(cmd); + sgx.cur_sg = scsi_sglist(cmd); + sgx.sp = sp; + + sg_prot = scsi_prot_sglist(cmd); + + while (qla24xx_get_one_block_sg(prot_int, &sgx, &partial)) { + + sle_dma = sgx.dma_addr; + sle_dma_len = sgx.dma_len; +alloc_and_fill: + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); + if (!dsd_ptr) + return 1; + + /* allocate new list */ + dsd_ptr->dsd_addr = next_dsd = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + + if (!next_dsd) { + /* + * Need to cleanup only this dsd_ptr, rest + * will be done by sp_free_dma() + */ + kfree(dsd_ptr); + return 1; + } + + list_add_tail(&dsd_ptr->list, + &((struct crc_context *)sp->u.scmd.ctx)->dsd_list); + + sp->flags |= SRB_CRC_CTX_DSD_VALID; + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = (uint32_t *)next_dsd; + } + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sle_dma_len); + avail_dsds--; + + if (partial == 0) { + /* Got a full protection interval */ + sle_dma = sg_dma_address(sg_prot) + tot_prot_dma_len; + sle_dma_len = 8; + + tot_prot_dma_len += sle_dma_len; + if (tot_prot_dma_len == sg_dma_len(sg_prot)) { + tot_prot_dma_len = 0; + sg_prot = sg_next(sg_prot); + } + + partial = 1; /* So as to not re-enter this block */ + goto alloc_and_fill; + } + } + /* Null termination */ + *cur_dsd++ = 0; + *cur_dsd++ = 0; + *cur_dsd++ = 0; + return 0; +} + +static int +qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, + uint16_t tot_dsds) +{ + void *next_dsd; + uint8_t avail_dsds = 0; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct scatterlist *sg; + uint32_t *cur_dsd = dsd; + int i; + uint16_t used_dsds = tot_dsds; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); + + uint8_t *cp; + + scsi_for_each_sg(cmd, sg, tot_dsds, i) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); + if (!dsd_ptr) + return 1; + + /* allocate new list */ + dsd_ptr->dsd_addr = next_dsd = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + + if (!next_dsd) { + /* + * Need to cleanup only this dsd_ptr, rest + * will be done by sp_free_dma() + */ + kfree(dsd_ptr); + return 1; + } + + list_add_tail(&dsd_ptr->list, + &((struct crc_context *)sp->u.scmd.ctx)->dsd_list); + + sp->flags |= SRB_CRC_CTX_DSD_VALID; + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = (uint32_t *)next_dsd; + } + sle_dma = sg_dma_address(sg); + ql_dbg(ql_dbg_io, vha, 0x300a, + "sg entry %d - addr=0x%x 0x%x, " "len=%d for cmd=%p.\n", + i, LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg), cmd); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + avail_dsds--; + + if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) { + cp = page_address(sg_page(sg)) + sg->offset; + ql_dbg(ql_dbg_io, vha, 0x300b, + "User data buffer=%p for cmd=%p.\n", cp, cmd); + } + } + /* Null termination */ + *cur_dsd++ = 0; + *cur_dsd++ = 0; + *cur_dsd++ = 0; + return 0; +} + +static int +qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, + uint32_t *dsd, + uint16_t tot_dsds) +{ + void *next_dsd; + uint8_t avail_dsds = 0; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct scatterlist *sg; + int i; + struct scsi_cmnd *cmd; + uint32_t *cur_dsd = dsd; + uint16_t used_dsds = tot_dsds; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + uint8_t *cp; + + cmd = GET_CMD_SP(sp); + scsi_for_each_prot_sg(cmd, sg, tot_dsds, i) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); + if (!dsd_ptr) + return 1; + + /* allocate new list */ + dsd_ptr->dsd_addr = next_dsd = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + + if (!next_dsd) { + /* + * Need to cleanup only this dsd_ptr, rest + * will be done by sp_free_dma() + */ + kfree(dsd_ptr); + return 1; + } + + list_add_tail(&dsd_ptr->list, + &((struct crc_context *)sp->u.scmd.ctx)->dsd_list); + + sp->flags |= SRB_CRC_CTX_DSD_VALID; + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = (uint32_t *)next_dsd; + } + sle_dma = sg_dma_address(sg); + if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) { + ql_dbg(ql_dbg_io, vha, 0x3027, + "%s(): %p, sg_entry %d - " + "addr=0x%x0x%x, len=%d.\n", + __func__, cur_dsd, i, + LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg)); + } + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + + if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) { + cp = page_address(sg_page(sg)) + sg->offset; + ql_dbg(ql_dbg_io, vha, 0x3028, + "%s(): Protection Data buffer = %p.\n", __func__, + cp); + } + avail_dsds--; + } + /* Null termination */ + *cur_dsd++ = 0; + *cur_dsd++ = 0; + *cur_dsd++ = 0; + return 0; +} + +/** + * qla24xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command + * Type 6 IOCB types. + * + * @sp: SRB command to process + * @cmd_pkt: Command type 3 IOCB + * @tot_dsds: Total number of segments to transfer + */ +static inline int +qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, + uint16_t tot_dsds, uint16_t tot_prot_dsds, uint16_t fw_prot_opts) +{ + uint32_t *cur_dsd, *fcp_dl; + scsi_qla_host_t *vha; + struct scsi_cmnd *cmd; + int sgc; + uint32_t total_bytes = 0; + uint32_t data_bytes; + uint32_t dif_bytes; + uint8_t bundling = 1; + uint16_t blk_size; + uint8_t *clr_ptr; + struct crc_context *crc_ctx_pkt = NULL; + struct qla_hw_data *ha; + uint8_t additional_fcpcdb_len; + uint16_t fcp_cmnd_len; + struct fcp_cmnd *fcp_cmnd; + dma_addr_t crc_ctx_dma; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + char tag[2]; +#endif + + cmd = GET_CMD_SP(sp); + + sgc = 0; + /* Update entry type to indicate Command Type CRC_2 IOCB */ + *((uint32_t *)(&cmd_pkt->entry_type)) = + __constant_cpu_to_le32(COMMAND_TYPE_CRC_2); + + vha = sp->fcport->vha; + ha = vha->hw; + + /* No data transfer */ + data_bytes = scsi_bufflen(cmd); + if (!data_bytes || cmd->sc_data_direction == DMA_NONE) { + cmd_pkt->byte_count = __constant_cpu_to_le32(0); + return QLA_SUCCESS; + } + + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_WRITE_DATA); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_READ_DATA); + } + + if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) || + (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP) || + (scsi_get_prot_op(cmd) == SCSI_PROT_READ_STRIP) || + (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_INSERT)) + bundling = 0; + + /* Allocate CRC context from global pool */ + crc_ctx_pkt = sp->u.scmd.ctx = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma); + + if (!crc_ctx_pkt) + goto crc_queuing_error; + + /* Zero out CTX area. */ + clr_ptr = (uint8_t *)crc_ctx_pkt; + memset(clr_ptr, 0, sizeof(*crc_ctx_pkt)); + + crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma; + + sp->flags |= SRB_CRC_CTX_DMA_VALID; + + /* Set handle */ + crc_ctx_pkt->handle = cmd_pkt->handle; + + INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list); + + qla24xx_set_t10dif_tags(sp, (struct fw_dif_context *) + &crc_ctx_pkt->ref_tag, tot_prot_dsds); + + cmd_pkt->crc_context_address[0] = cpu_to_le32(LSD(crc_ctx_dma)); + cmd_pkt->crc_context_address[1] = cpu_to_le32(MSD(crc_ctx_dma)); + cmd_pkt->crc_context_len = CRC_CONTEXT_LEN_FW; + + /* Determine SCSI command length -- align to 4 byte boundary */ + if (cmd->cmd_len > 16) { + additional_fcpcdb_len = cmd->cmd_len - 16; + if ((cmd->cmd_len % 4) != 0) { + /* SCSI cmd > 16 bytes must be multiple of 4 */ + goto crc_queuing_error; + } + fcp_cmnd_len = 12 + cmd->cmd_len + 4; + } else { + additional_fcpcdb_len = 0; + fcp_cmnd_len = 12 + 16 + 4; + } + + fcp_cmnd = &crc_ctx_pkt->fcp_cmnd; + + fcp_cmnd->additional_cdb_len = additional_fcpcdb_len; + if (cmd->sc_data_direction == DMA_TO_DEVICE) + fcp_cmnd->additional_cdb_len |= 1; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + fcp_cmnd->additional_cdb_len |= 2; + + int_to_scsilun(cmd->device->lun, &fcp_cmnd->lun); + memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len); + cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len); + cmd_pkt->fcp_cmnd_dseg_address[0] = cpu_to_le32( + LSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF)); + cmd_pkt->fcp_cmnd_dseg_address[1] = cpu_to_le32( + MSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF)); + fcp_cmnd->task_management = 0; + + /* + * Update tagged queuing modifier if using command tag queuing + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + if (scsi_populate_tag_msg(cmd, tag)) { + switch (tag[0]) { + case HEAD_OF_QUEUE_TAG: + fcp_cmnd->task_attribute = TSK_HEAD_OF_QUEUE; + break; + case ORDERED_QUEUE_TAG: + fcp_cmnd->task_attribute = TSK_ORDERED; + break; + default: + fcp_cmnd->task_attribute = 0; + break; + } + } else { + fcp_cmnd->task_attribute = 0; + } +#else + fcp_cmnd->task_attribute = 0; +#endif + + cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */ + + /* Compute dif len and adjust data len to incude protection */ + dif_bytes = 0; + blk_size = cmd->device->sector_size; + dif_bytes = (data_bytes / blk_size) * 8; + + switch (scsi_get_prot_op(GET_CMD_SP(sp))) { + case SCSI_PROT_READ_INSERT: + case SCSI_PROT_WRITE_STRIP: + total_bytes = data_bytes; + data_bytes += dif_bytes; + break; + + case SCSI_PROT_READ_STRIP: + case SCSI_PROT_WRITE_INSERT: + case SCSI_PROT_READ_PASS: + case SCSI_PROT_WRITE_PASS: + total_bytes = data_bytes + dif_bytes; + break; + default: + BUG(); + } + + if (!qla2x00_hba_err_chk_enabled(sp)) + fw_prot_opts |= 0x10; /* Disable Guard tag checking */ + + if (!bundling) { + cur_dsd = (uint32_t *) &crc_ctx_pkt->u.nobundling.data_address; + } else { + /* + * Configure Bundling if we need to fetch interlaving + * protection PCI accesses + */ + fw_prot_opts |= PO_ENABLE_DIF_BUNDLING; + crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes); + crc_ctx_pkt->u.bundling.dseg_count = cpu_to_le16(tot_dsds - + tot_prot_dsds); + cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.data_address; + } + + /* Finish the common fields of CRC pkt */ + crc_ctx_pkt->blk_size = cpu_to_le16(blk_size); + crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts); + crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes); + crc_ctx_pkt->guard_seed = __constant_cpu_to_le16(0); + /* Fibre channel byte count */ + cmd_pkt->byte_count = cpu_to_le32(total_bytes); + fcp_dl = (uint32_t *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 + + additional_fcpcdb_len); + *fcp_dl = htonl(total_bytes); + + if (!data_bytes || cmd->sc_data_direction == DMA_NONE) { + cmd_pkt->byte_count = __constant_cpu_to_le32(0); + return QLA_SUCCESS; + } + /* Walks data segments */ + + cmd_pkt->control_flags |= + __constant_cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE); + + if (!bundling && tot_prot_dsds) { + if (qla24xx_walk_and_build_sglist_no_difb(ha, sp, + cur_dsd, tot_dsds)) + goto crc_queuing_error; + } else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd, + (tot_dsds - tot_prot_dsds))) + goto crc_queuing_error; + + if (bundling && tot_prot_dsds) { + /* Walks dif segments */ + cmd_pkt->control_flags |= + __constant_cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE); + cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address; + if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd, + tot_prot_dsds)) + goto crc_queuing_error; + } + return QLA_SUCCESS; + +crc_queuing_error: + /* Cleanup will be performed by the caller */ + + return QLA_FUNCTION_FAILED; +} /** * qla24xx_start_scsi() - Send a SCSI command to the ISP @@ -678,8 +1524,6 @@ qla24xx_start_scsi(srb_t *sp) { int ret, nseg; unsigned long flags; - scsi_qla_host_t *ha; - struct scsi_cmnd *cmd; uint32_t *clr_ptr; uint32_t index; uint32_t handle; @@ -687,34 +1531,844 @@ qla24xx_start_scsi(srb_t *sp) uint16_t cnt; uint16_t req_cnt; uint16_t tot_dsds; - struct device_reg_24xx __iomem *reg; + struct req_que *req = NULL; + struct rsp_que *rsp = NULL; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + struct scsi_qla_host *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + char tag[2]; +#endif /* Setup device pointers. */ ret = 0; - ha = sp->ha; - reg = &ha->iobase->isp24; - cmd = sp->cmd; + + qla25xx_set_que(sp, &rsp); + req = vha->req; + /* So we know we haven't pci_map'ed anything yet */ tot_dsds = 0; /* Send marker if required */ - if (ha->marker_needed != 0) { - if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) { + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) return QLA_FUNCTION_FAILED; - } - ha->marker_needed = 0; + vha->marker_needed = 0; } /* Acquire ring specific lock */ spin_lock_irqsave(&ha->hardware_lock, flags); /* Check for room in outstanding command list. */ - handle = ha->current_outstanding_cmd; + handle = req->current_outstanding_cmd; for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { handle++; if (handle == MAX_OUTSTANDING_COMMANDS) handle = 1; - if (!ha->outstanding_cmds[handle]) + if (!req->outstanding_cmds[handle]) + break; + } + if (index == MAX_OUTSTANDING_COMMANDS) { + goto queuing_error; + } + + /* Map the sg table so we have an accurate count of sg entries needed */ + if (scsi_sg_count(cmd)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), + scsi_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + } else + nseg = 0; + + tot_dsds = nseg; + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + if (req->cnt < (req_cnt + 2)) { + cnt = RD_REG_DWORD_RELAXED(req->req_q_out); + + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + if (req->cnt < (req_cnt + 2)) + goto queuing_error; + } + + /* Build command packet. */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; + req->cnt -= req_cnt; + + cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + + int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + /* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */ + if (scsi_populate_tag_msg(cmd, tag)) { + switch (tag[0]) { + case HEAD_OF_QUEUE_TAG: + cmd_pkt->task = TSK_HEAD_OF_QUEUE; + break; + case ORDERED_QUEUE_TAG: + cmd_pkt->task = TSK_ORDERED; + break; + } + } +#endif + + /* Load SCSI command packet. */ + memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); + host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); + + cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + + /* Build IOCB segments */ + qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds); + + /* Set total data segment count. */ + cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Specify response queue number where completion should happen */ + cmd_pkt->entry_status = (uint8_t) rsp->id; + wmb(); + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + sp->flags |= SRB_DMA_VALID; + + /* Set chip new ring index. */ + WRT_REG_DWORD(req->req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr); + + /* Manage unprocessed RIO/ZIO commands in response queue. */ + if (vha->flags.process_response_queue && + rsp->ring_ptr->signature != RESPONSE_PROCESSED) + qla24xx_process_response_queue(vha, rsp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return QLA_SUCCESS; + +queuing_error: + if (tot_dsds) + scsi_dma_unmap(cmd); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_FUNCTION_FAILED; +} + + +/** + * qla24xx_dif_start_scsi() - Send a SCSI command to the ISP + * @sp: command to send to the ISP + * + * Returns non-zero if a failure occurred, else zero. + */ +int +qla24xx_dif_start_scsi(srb_t *sp) +{ + int nseg; + unsigned long flags; + uint32_t *clr_ptr; + uint32_t index; + uint32_t handle; + uint16_t cnt; + uint16_t req_cnt = 0; + uint16_t tot_dsds; + uint16_t tot_prot_dsds; + uint16_t fw_prot_opts = 0; + struct req_que *req = NULL; + struct rsp_que *rsp = NULL; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + struct scsi_qla_host *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct cmd_type_crc_2 *cmd_pkt; + uint32_t status = 0; + +#define QDSS_GOT_Q_SPACE BIT_0 + + /* Only process protection or >16 cdb in this routine */ + if (scsi_get_prot_op(cmd) == SCSI_PROT_NORMAL) { + if (cmd->cmd_len <= 16) + return qla24xx_start_scsi(sp); + } + + /* Setup device pointers. */ + + qla25xx_set_que(sp, &rsp); + req = vha->req; + + /* So we know we haven't pci_map'ed anything yet */ + tot_dsds = 0; + + /* Send marker if required */ + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) + return QLA_FUNCTION_FAILED; + vha->marker_needed = 0; + } + + /* Acquire ring specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Check for room in outstanding command list. */ + handle = req->current_outstanding_cmd; + for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + handle++; + if (handle == MAX_OUTSTANDING_COMMANDS) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + + if (index == MAX_OUTSTANDING_COMMANDS) + goto queuing_error; + + /* Compute number of required data segments */ + /* Map the sg table so we have an accurate count of sg entries needed */ + if (scsi_sg_count(cmd)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), + scsi_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + else + sp->flags |= SRB_DMA_VALID; + + if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) || + (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP)) { + struct qla2_sgx sgx; + uint32_t partial; + + memset(&sgx, 0, sizeof(struct qla2_sgx)); + sgx.tot_bytes = scsi_bufflen(cmd); + sgx.cur_sg = scsi_sglist(cmd); + sgx.sp = sp; + + nseg = 0; + while (qla24xx_get_one_block_sg( + cmd->device->sector_size, &sgx, &partial)) + nseg++; + } + } else + nseg = 0; + + /* number of required data segments */ + tot_dsds = nseg; + + /* Compute number of required protection segments */ + if (qla24xx_configure_prot_mode(sp, &fw_prot_opts)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_prot_sglist(cmd), + scsi_prot_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + else + sp->flags |= SRB_CRC_PROT_DMA_VALID; + + if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) || + (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP)) { + nseg = scsi_bufflen(cmd) / cmd->device->sector_size; + } + } else { + nseg = 0; + } + + req_cnt = 1; + /* Total Data and protection sg segment(s) */ + tot_prot_dsds = nseg; + tot_dsds += nseg; + if (req->cnt < (req_cnt + 2)) { + cnt = RD_REG_DWORD_RELAXED(req->req_q_out); + + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + if (req->cnt < (req_cnt + 2)) + goto queuing_error; + } + + status |= QDSS_GOT_Q_SPACE; + + /* Build header part of command packet (excluding the OPCODE). */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; + req->cnt -= req_cnt; + + /* Fill-in common area */ + cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + + int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + + /* Total Data and protection segment(s) */ + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Build IOCB segments and adjust for data protection segments */ + if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *) + req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) != + QLA_SUCCESS) + goto queuing_error; + + cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Specify response queue number where completion should happen */ + cmd_pkt->entry_status = (uint8_t) rsp->id; + cmd_pkt->timeout = __constant_cpu_to_le16(0); + wmb(); + + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + /* Set chip new ring index. */ + WRT_REG_DWORD(req->req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr); + + /* Manage unprocessed RIO/ZIO commands in response queue. */ + if (vha->flags.process_response_queue && + rsp->ring_ptr->signature != RESPONSE_PROCESSED) + qla24xx_process_response_queue(vha, rsp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_SUCCESS; + +queuing_error: + if (status & QDSS_GOT_Q_SPACE) { + req->outstanding_cmds[handle] = NULL; + req->cnt += req_cnt; + } + /* Cleanup will be performed by the caller (queuecommand) */ + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return QLA_FUNCTION_FAILED; +} + + +static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp) +{ + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + struct qla_hw_data *ha = sp->fcport->vha->hw; + int affinity = cmd->request->cpu; + + if (ha->flags.cpu_affinity_enabled && affinity >= 0 && + affinity < ha->max_rsp_queues - 1) + *rsp = ha->rsp_q_map[affinity + 1]; + else + *rsp = ha->rsp_q_map[0]; +} + +/* Generic Control-SRB manipulation functions. */ +void * +qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp) +{ + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); + uint32_t index, handle; + request_t *pkt; + uint16_t cnt, req_cnt; + + pkt = NULL; + req_cnt = 1; + handle = 0; + + if (!sp) + goto skip_cmd_array; + + /* Check for room in outstanding command list. */ + handle = req->current_outstanding_cmd; + for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + handle++; + if (handle == MAX_OUTSTANDING_COMMANDS) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + if (index == MAX_OUTSTANDING_COMMANDS) { + ql_log(ql_log_warn, vha, 0x700b, + "No room on oustanding cmd array.\n"); + goto queuing_error; + } + + /* Prep command array. */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + + /* Adjust entry-counts as needed. */ + if (sp->type != SRB_SCSI_CMD) { + req_cnt = sp->iocbs; + } + +skip_cmd_array: + /* Check for room on request queue. */ + if (req->cnt < req_cnt) { + if (ha->mqenable || IS_QLA83XX(ha)) + cnt = RD_REG_DWORD(®->isp25mq.req_q_out); + else if (IS_QLA82XX(ha)) + cnt = RD_REG_DWORD(®->isp82.req_q_out); + else if (IS_FWI2_CAPABLE(ha)) + cnt = RD_REG_DWORD(®->isp24.req_q_out); + else + cnt = qla2x00_debounce_register( + ISP_REQ_Q_OUT(ha, ®->isp)); + + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + } + if (req->cnt < req_cnt) + goto queuing_error; + + /* Prep packet */ + req->cnt -= req_cnt; + pkt = req->ring_ptr; + memset(pkt, 0, REQUEST_ENTRY_SIZE); + pkt->entry_count = req_cnt; + pkt->handle = handle; + +queuing_error: + return pkt; +} +EXPORT_SYMBOL(qla2x00_alloc_iocbs); + +static void +qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) +{ + struct srb_iocb *lio = &sp->u.iocb_cmd; + + logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; + logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI); + if (lio->u.logio.flags & SRB_LOGIN_COND_PLOGI) + logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI); + if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI) + logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI); + logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); + logio->port_id[0] = sp->fcport->d_id.b.al_pa; + logio->port_id[1] = sp->fcport->d_id.b.area; + logio->port_id[2] = sp->fcport->d_id.b.domain; + logio->vp_index = sp->fcport->vha->vp_idx; +} + +static void +qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx) +{ + struct qla_hw_data *ha = sp->fcport->vha->hw; + struct srb_iocb *lio = &sp->u.iocb_cmd; + uint16_t opts; + + mbx->entry_type = MBX_IOCB_TYPE; + SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id); + mbx->mb0 = cpu_to_le16(MBC_LOGIN_FABRIC_PORT); + opts = lio->u.logio.flags & SRB_LOGIN_COND_PLOGI ? BIT_0 : 0; + opts |= lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI ? BIT_1 : 0; + if (HAS_EXTENDED_IDS(ha)) { + mbx->mb1 = cpu_to_le16(sp->fcport->loop_id); + mbx->mb10 = cpu_to_le16(opts); + } else { + mbx->mb1 = cpu_to_le16((sp->fcport->loop_id << 8) | opts); + } + mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain); + mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 | + sp->fcport->d_id.b.al_pa); + mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); +} + +static void +qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) +{ + logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; + logio->control_flags = + cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); + logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); + logio->port_id[0] = sp->fcport->d_id.b.al_pa; + logio->port_id[1] = sp->fcport->d_id.b.area; + logio->port_id[2] = sp->fcport->d_id.b.domain; + logio->vp_index = sp->fcport->vha->vp_idx; +} + +static void +qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx) +{ + struct qla_hw_data *ha = sp->fcport->vha->hw; + + mbx->entry_type = MBX_IOCB_TYPE; + SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id); + mbx->mb0 = cpu_to_le16(MBC_LOGOUT_FABRIC_PORT); + mbx->mb1 = HAS_EXTENDED_IDS(ha) ? + cpu_to_le16(sp->fcport->loop_id): + cpu_to_le16(sp->fcport->loop_id << 8); + mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain); + mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 | + sp->fcport->d_id.b.al_pa); + mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); + /* Implicit: mbx->mbx10 = 0. */ +} + +static void +qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio) +{ + logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; + logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC); + logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); + logio->vp_index = sp->fcport->vha->vp_idx; +} + +static void +qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx) +{ + struct qla_hw_data *ha = sp->fcport->vha->hw; + + mbx->entry_type = MBX_IOCB_TYPE; + SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id); + mbx->mb0 = cpu_to_le16(MBC_GET_PORT_DATABASE); + if (HAS_EXTENDED_IDS(ha)) { + mbx->mb1 = cpu_to_le16(sp->fcport->loop_id); + mbx->mb10 = cpu_to_le16(BIT_0); + } else { + mbx->mb1 = cpu_to_le16((sp->fcport->loop_id << 8) | BIT_0); + } + mbx->mb2 = cpu_to_le16(MSW(ha->async_pd_dma)); + mbx->mb3 = cpu_to_le16(LSW(ha->async_pd_dma)); + mbx->mb6 = cpu_to_le16(MSW(MSD(ha->async_pd_dma))); + mbx->mb7 = cpu_to_le16(LSW(MSD(ha->async_pd_dma))); + mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); +} + +static void +qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) +{ + uint32_t flags; + unsigned int lun; + struct fc_port *fcport = sp->fcport; + scsi_qla_host_t *vha = fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct srb_iocb *iocb = &sp->u.iocb_cmd; + struct req_que *req = vha->req; + + flags = iocb->u.tmf.flags; + lun = iocb->u.tmf.lun; + + tsk->entry_type = TSK_MGMT_IOCB_TYPE; + tsk->entry_count = 1; + tsk->handle = MAKE_HANDLE(req->id, tsk->handle); + tsk->nport_handle = cpu_to_le16(fcport->loop_id); + tsk->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); + tsk->control_flags = cpu_to_le32(flags); + tsk->port_id[0] = fcport->d_id.b.al_pa; + tsk->port_id[1] = fcport->d_id.b.area; + tsk->port_id[2] = fcport->d_id.b.domain; + tsk->vp_index = fcport->vha->vp_idx; + + if (flags == TCF_LUN_RESET) { + int_to_scsilun(lun, &tsk->lun); + host_to_fcp_swap((uint8_t *)&tsk->lun, + sizeof(tsk->lun)); + } +} + +static void +qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) +{ + struct fc_bsg_job *bsg_job = sp->u.bsg_job; + + els_iocb->entry_type = ELS_IOCB_TYPE; + els_iocb->entry_count = 1; + els_iocb->sys_define = 0; + els_iocb->entry_status = 0; + els_iocb->handle = sp->handle; + els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); + els_iocb->tx_dsd_count = __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt); + els_iocb->vp_index = sp->fcport->vha->vp_idx; + els_iocb->sof_type = EST_SOFI3; + els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt); + + els_iocb->opcode = + sp->type == SRB_ELS_CMD_RPT ? + bsg_job->request->rqst_data.r_els.els_code : + bsg_job->request->rqst_data.h_els.command_code; + els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa; + els_iocb->port_id[1] = sp->fcport->d_id.b.area; + els_iocb->port_id[2] = sp->fcport->d_id.b.domain; + els_iocb->control_flags = 0; + els_iocb->rx_byte_count = + cpu_to_le32(bsg_job->reply_payload.payload_len); + els_iocb->tx_byte_count = + cpu_to_le32(bsg_job->request_payload.payload_len); + + els_iocb->tx_address[0] = cpu_to_le32(LSD(sg_dma_address + (bsg_job->request_payload.sg_list))); + els_iocb->tx_address[1] = cpu_to_le32(MSD(sg_dma_address + (bsg_job->request_payload.sg_list))); + els_iocb->tx_len = cpu_to_le32(sg_dma_len + (bsg_job->request_payload.sg_list)); + + els_iocb->rx_address[0] = cpu_to_le32(LSD(sg_dma_address + (bsg_job->reply_payload.sg_list))); + els_iocb->rx_address[1] = cpu_to_le32(MSD(sg_dma_address + (bsg_job->reply_payload.sg_list))); + els_iocb->rx_len = cpu_to_le32(sg_dma_len + (bsg_job->reply_payload.sg_list)); +} + +static void +qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) +{ + uint16_t avail_dsds; + uint32_t *cur_dsd; + struct scatterlist *sg; + int index; + uint16_t tot_dsds; + scsi_qla_host_t *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; + int loop_iterartion = 0; + int cont_iocb_prsnt = 0; + int entry_count = 1; + + memset(ct_iocb, 0, sizeof(ms_iocb_entry_t)); + ct_iocb->entry_type = CT_IOCB_TYPE; + ct_iocb->entry_status = 0; + ct_iocb->handle1 = sp->handle; + SET_TARGET_ID(ha, ct_iocb->loop_id, sp->fcport->loop_id); + ct_iocb->status = __constant_cpu_to_le16(0); + ct_iocb->control_flags = __constant_cpu_to_le16(0); + ct_iocb->timeout = 0; + ct_iocb->cmd_dsd_count = + __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt); + ct_iocb->total_dsd_count = + __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt + 1); + ct_iocb->req_bytecount = + cpu_to_le32(bsg_job->request_payload.payload_len); + ct_iocb->rsp_bytecount = + cpu_to_le32(bsg_job->reply_payload.payload_len); + + ct_iocb->dseg_req_address[0] = cpu_to_le32(LSD(sg_dma_address + (bsg_job->request_payload.sg_list))); + ct_iocb->dseg_req_address[1] = cpu_to_le32(MSD(sg_dma_address + (bsg_job->request_payload.sg_list))); + ct_iocb->dseg_req_length = ct_iocb->req_bytecount; + + ct_iocb->dseg_rsp_address[0] = cpu_to_le32(LSD(sg_dma_address + (bsg_job->reply_payload.sg_list))); + ct_iocb->dseg_rsp_address[1] = cpu_to_le32(MSD(sg_dma_address + (bsg_job->reply_payload.sg_list))); + ct_iocb->dseg_rsp_length = ct_iocb->rsp_bytecount; + + avail_dsds = 1; + cur_dsd = (uint32_t *)ct_iocb->dseg_rsp_address; + index = 0; + tot_dsds = bsg_job->reply_payload.sg_cnt; + + for_each_sg(bsg_job->reply_payload.sg_list, sg, tot_dsds, index) { + dma_addr_t sle_dma; + cont_a64_entry_t *cont_pkt; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + /* + * Five DSDs are available in the Cont. + * Type 1 IOCB. + */ + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + vha->hw->req_q_map[0]); + cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; + avail_dsds = 5; + cont_iocb_prsnt = 1; + entry_count++; + } + + sle_dma = sg_dma_address(sg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + loop_iterartion++; + avail_dsds--; + } + ct_iocb->entry_count = entry_count; +} + +static void +qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) +{ + uint16_t avail_dsds; + uint32_t *cur_dsd; + struct scatterlist *sg; + int index; + uint16_t tot_dsds; + scsi_qla_host_t *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; + int loop_iterartion = 0; + int cont_iocb_prsnt = 0; + int entry_count = 1; + + ct_iocb->entry_type = CT_IOCB_TYPE; + ct_iocb->entry_status = 0; + ct_iocb->sys_define = 0; + ct_iocb->handle = sp->handle; + + ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); + ct_iocb->vp_index = sp->fcport->vha->vp_idx; + ct_iocb->comp_status = __constant_cpu_to_le16(0); + + ct_iocb->cmd_dsd_count = + __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt); + ct_iocb->timeout = 0; + ct_iocb->rsp_dsd_count = + __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt); + ct_iocb->rsp_byte_count = + cpu_to_le32(bsg_job->reply_payload.payload_len); + ct_iocb->cmd_byte_count = + cpu_to_le32(bsg_job->request_payload.payload_len); + ct_iocb->dseg_0_address[0] = cpu_to_le32(LSD(sg_dma_address + (bsg_job->request_payload.sg_list))); + ct_iocb->dseg_0_address[1] = cpu_to_le32(MSD(sg_dma_address + (bsg_job->request_payload.sg_list))); + ct_iocb->dseg_0_len = cpu_to_le32(sg_dma_len + (bsg_job->request_payload.sg_list)); + + avail_dsds = 1; + cur_dsd = (uint32_t *)ct_iocb->dseg_1_address; + index = 0; + tot_dsds = bsg_job->reply_payload.sg_cnt; + + for_each_sg(bsg_job->reply_payload.sg_list, sg, tot_dsds, index) { + dma_addr_t sle_dma; + cont_a64_entry_t *cont_pkt; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + /* + * Five DSDs are available in the Cont. + * Type 1 IOCB. + */ + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + ha->req_q_map[0]); + cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; + avail_dsds = 5; + cont_iocb_prsnt = 1; + entry_count++; + } + + sle_dma = sg_dma_address(sg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + loop_iterartion++; + avail_dsds--; + } + ct_iocb->entry_count = entry_count; +} + +/* + * qla82xx_start_scsi() - Send a SCSI command to the ISP + * @sp: command to send to the ISP + * + * Returns non-zero if a failure occurred, else zero. + */ +int +qla82xx_start_scsi(srb_t *sp) +{ + int ret, nseg; + unsigned long flags; + struct scsi_cmnd *cmd; + uint32_t *clr_ptr; + uint32_t index; + uint32_t handle; + uint16_t cnt; + uint16_t req_cnt; + uint16_t tot_dsds; + struct device_reg_82xx __iomem *reg; + uint32_t dbval; + uint32_t *fcp_dl; + uint8_t additional_cdb_len; + struct ct6_dsd *ctx; + struct scsi_qla_host *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = NULL; + struct rsp_que *rsp = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + char tag[2]; +#endif + + /* Setup device pointers. */ + ret = 0; + reg = &ha->iobase->isp82; + cmd = GET_CMD_SP(sp); + req = vha->req; + rsp = ha->rsp_q_map[0]; + + /* So we know we haven't pci_map'ed anything yet */ + tot_dsds = 0; + + dbval = 0x04 | (ha->portnum << 5); + + /* Send marker if required */ + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, + rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x300c, + "qla2x00_marker failed for cmd=%p.\n", cmd); + return QLA_FUNCTION_FAILED; + } + vha->marker_needed = 0; + } + + /* Acquire ring specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Check for room in outstanding command list. */ + handle = req->current_outstanding_cmd; + for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + handle++; + if (handle == MAX_OUTSTANDING_COMMANDS) + handle = 1; + if (!req->outstanding_cmds[handle]) break; } if (index == MAX_OUTSTANDING_COMMANDS) @@ -731,84 +2385,361 @@ qla24xx_start_scsi(srb_t *sp) tot_dsds = nseg; - req_cnt = qla24xx_calc_iocbs(tot_dsds); - if (ha->req_q_cnt < (req_cnt + 2)) { - cnt = (uint16_t)RD_REG_DWORD_RELAXED(®->req_q_out); - if (ha->req_ring_index < cnt) - ha->req_q_cnt = cnt - ha->req_ring_index; + if (tot_dsds > ql2xshiftctondsd) { + struct cmd_type_6 *cmd_pkt; + uint16_t more_dsd_lists = 0; + struct dsd_dma *dsd_ptr; + uint16_t i; + + more_dsd_lists = qla24xx_calc_dsd_lists(tot_dsds); + if ((more_dsd_lists + ha->gbl_dsd_inuse) >= NUM_DSD_CHAIN) { + ql_dbg(ql_dbg_io, vha, 0x300d, + "Num of DSD list %d is than %d for cmd=%p.\n", + more_dsd_lists + ha->gbl_dsd_inuse, NUM_DSD_CHAIN, + cmd); + goto queuing_error; + } + + if (more_dsd_lists <= ha->gbl_dsd_avail) + goto sufficient_dsds; else - ha->req_q_cnt = ha->request_q_length - - (ha->req_ring_index - cnt); + more_dsd_lists -= ha->gbl_dsd_avail; + + for (i = 0; i < more_dsd_lists; i++) { + dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); + if (!dsd_ptr) { + ql_log(ql_log_fatal, vha, 0x300e, + "Failed to allocate memory for dsd_dma " + "for cmd=%p.\n", cmd); + goto queuing_error; + } + + dsd_ptr->dsd_addr = dma_pool_alloc(ha->dl_dma_pool, + GFP_ATOMIC, &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + kfree(dsd_ptr); + ql_log(ql_log_fatal, vha, 0x300f, + "Failed to allocate memory for dsd_addr " + "for cmd=%p.\n", cmd); + goto queuing_error; + } + list_add_tail(&dsd_ptr->list, &ha->gbl_dsd_list); + ha->gbl_dsd_avail++; + } + +sufficient_dsds: + req_cnt = 1; + + if (req->cnt < (req_cnt + 2)) { + cnt = (uint16_t)RD_REG_DWORD_RELAXED( + ®->req_q_out[0]); + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + if (req->cnt < (req_cnt + 2)) + goto queuing_error; + } + + ctx = sp->u.scmd.ctx = + mempool_alloc(ha->ctx_mempool, GFP_ATOMIC); + if (!ctx) { + ql_log(ql_log_fatal, vha, 0x3010, + "Failed to allocate ctx for cmd=%p.\n", cmd); + goto queuing_error; + } + + memset(ctx, 0, sizeof(struct ct6_dsd)); + ctx->fcp_cmnd = dma_pool_alloc(ha->fcp_cmnd_dma_pool, + GFP_ATOMIC, &ctx->fcp_cmnd_dma); + if (!ctx->fcp_cmnd) { + ql_log(ql_log_fatal, vha, 0x3011, + "Failed to allocate fcp_cmnd for cmd=%p.\n", cmd); + goto queuing_error_fcp_cmnd; + } + + /* Initialize the DSD list and dma handle */ + INIT_LIST_HEAD(&ctx->dsd_list); + ctx->dsd_use_cnt = 0; + + if (cmd->cmd_len > 16) { + additional_cdb_len = cmd->cmd_len - 16; + if ((cmd->cmd_len % 4) != 0) { + /* SCSI command bigger than 16 bytes must be + * multiple of 4 + */ + ql_log(ql_log_warn, vha, 0x3012, + "scsi cmd len %d not multiple of 4 " + "for cmd=%p.\n", cmd->cmd_len, cmd); + goto queuing_error_fcp_cmnd; + } + ctx->fcp_cmnd_len = 12 + cmd->cmd_len + 4; + } else { + additional_cdb_len = 0; + ctx->fcp_cmnd_len = 12 + 16 + 4; + } + + cmd_pkt = (struct cmd_type_6 *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + + /* Build IOCB segments */ + if (qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds)) + goto queuing_error_fcp_cmnd; + + int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + + /* build FCP_CMND IU */ + memset(ctx->fcp_cmnd, 0, sizeof(struct fcp_cmnd)); + int_to_scsilun(cmd->device->lun, &ctx->fcp_cmnd->lun); + ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len; + + if (cmd->sc_data_direction == DMA_TO_DEVICE) + ctx->fcp_cmnd->additional_cdb_len |= 1; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + ctx->fcp_cmnd->additional_cdb_len |= 2; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + /* + * Update tagged queuing modifier -- default is TSK_SIMPLE (0). + */ + if (scsi_populate_tag_msg(cmd, tag)) { + switch (tag[0]) { + case HEAD_OF_QUEUE_TAG: + ctx->fcp_cmnd->task_attribute = + TSK_HEAD_OF_QUEUE; + break; + case ORDERED_QUEUE_TAG: + ctx->fcp_cmnd->task_attribute = + TSK_ORDERED; + break; + } + } +#endif + + /* Populate the FCP_PRIO. */ + if (ha->flags.fcp_prio_enabled) + ctx->fcp_cmnd->task_attribute |= + sp->fcport->fcp_prio << 3; + + memcpy(ctx->fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len); + + fcp_dl = (uint32_t *)(ctx->fcp_cmnd->cdb + 16 + + additional_cdb_len); + *fcp_dl = htonl((uint32_t)scsi_bufflen(cmd)); + + cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(ctx->fcp_cmnd_len); + cmd_pkt->fcp_cmnd_dseg_address[0] = + cpu_to_le32(LSD(ctx->fcp_cmnd_dma)); + cmd_pkt->fcp_cmnd_dseg_address[1] = + cpu_to_le32(MSD(ctx->fcp_cmnd_dma)); + + sp->flags |= SRB_FCP_CMND_DMA_VALID; + cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + /* Set total data segment count. */ + cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Specify response queue number where + * completion should happen + */ + cmd_pkt->entry_status = (uint8_t) rsp->id; + } else { + struct cmd_type_7 *cmd_pkt; + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + if (req->cnt < (req_cnt + 2)) { + cnt = (uint16_t)RD_REG_DWORD_RELAXED( + ®->req_q_out[0]); + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + } + if (req->cnt < (req_cnt + 2)) + goto queuing_error; + + cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0).*/ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + + int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, + sizeof(cmd_pkt->lun)); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + /* + * Update tagged queuing modifier -- default is TSK_SIMPLE (0). + */ + if (scsi_populate_tag_msg(cmd, tag)) { + switch (tag[0]) { + case HEAD_OF_QUEUE_TAG: + cmd_pkt->task = TSK_HEAD_OF_QUEUE; + break; + case ORDERED_QUEUE_TAG: + cmd_pkt->task = TSK_ORDERED; + break; + } + } +#endif + + /* Populate the FCP_PRIO. */ + if (ha->flags.fcp_prio_enabled) + cmd_pkt->task |= sp->fcport->fcp_prio << 3; + + /* Load SCSI command packet. */ + memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); + host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); + + cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + + /* Build IOCB segments */ + qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds); + + /* Set total data segment count. */ + cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Specify response queue number where + * completion should happen. + */ + cmd_pkt->entry_status = (uint8_t) rsp->id; + } - if (ha->req_q_cnt < (req_cnt + 2)) - goto queuing_error; - /* Build command packet. */ - ha->current_outstanding_cmd = handle; - ha->outstanding_cmds[handle] = sp; - sp->ha = ha; - sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; - ha->req_q_cnt -= req_cnt; - - cmd_pkt = (struct cmd_type_7 *)ha->request_ring_ptr; - cmd_pkt->handle = handle; - - /* Zero out remaining portion of packet. */ - /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ - clr_ptr = (uint32_t *)cmd_pkt + 2; - memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); - cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); - - /* Set NPORT-ID and LUN number*/ - cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; - cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; - cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->fcport->vp_idx; - - int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun); - host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); - - /* Load SCSI command packet. */ - memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); - host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); - - cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); - - /* Build IOCB segments */ - qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds); - - /* Set total data segment count. */ - cmd_pkt->entry_count = (uint8_t)req_cnt; + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; + req->cnt -= req_cnt; wmb(); /* Adjust ring index. */ - ha->req_ring_index++; - if (ha->req_ring_index == ha->request_q_length) { - ha->req_ring_index = 0; - ha->request_ring_ptr = ha->request_ring; + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; } else - ha->request_ring_ptr++; + req->ring_ptr++; sp->flags |= SRB_DMA_VALID; /* Set chip new ring index. */ - WRT_REG_DWORD(®->req_q_in, ha->req_ring_index); - RD_REG_DWORD_RELAXED(®->req_q_in); /* PCI Posting. */ + /* write, read and verify logic */ + dbval = dbval | (req->id << 8) | (req->ring_index << 16); + if (ql2xdbwr) + qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval); + else { + WRT_REG_DWORD( + (unsigned long __iomem *)ha->nxdb_wr_ptr, + dbval); + wmb(); + while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + WRT_REG_DWORD( + (unsigned long __iomem *)ha->nxdb_wr_ptr, + dbval); + wmb(); + } + } /* Manage unprocessed RIO/ZIO commands in response queue. */ - if (ha->flags.process_response_queue && - ha->response_ring_ptr->signature != RESPONSE_PROCESSED) - qla24xx_process_response_queue(ha); + if (vha->flags.process_response_queue && + rsp->ring_ptr->signature != RESPONSE_PROCESSED) + qla24xx_process_response_queue(vha, rsp); spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_SUCCESS; +queuing_error_fcp_cmnd: + dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, ctx->fcp_cmnd_dma); queuing_error: if (tot_dsds) scsi_dma_unmap(cmd); + if (sp->u.scmd.ctx) { + mempool_free(sp->u.scmd.ctx, ha->ctx_mempool); + sp->u.scmd.ctx = NULL; + } spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_FUNCTION_FAILED; } + +int +qla2x00_start_sp(srb_t *sp) +{ + int rval; + struct qla_hw_data *ha = sp->fcport->vha->hw; + void *pkt; + unsigned long flags; + + rval = QLA_FUNCTION_FAILED; + spin_lock_irqsave(&ha->hardware_lock, flags); + pkt = qla2x00_alloc_iocbs(sp->fcport->vha, sp); + if (!pkt) { + ql_log(ql_log_warn, sp->fcport->vha, 0x700c, + "qla2x00_alloc_iocbs failed.\n"); + goto done; + } + + rval = QLA_SUCCESS; + switch (sp->type) { + case SRB_LOGIN_CMD: + IS_FWI2_CAPABLE(ha) ? + qla24xx_login_iocb(sp, pkt): + qla2x00_login_iocb(sp, pkt); + break; + case SRB_LOGOUT_CMD: + IS_FWI2_CAPABLE(ha) ? + qla24xx_logout_iocb(sp, pkt): + qla2x00_logout_iocb(sp, pkt); + break; + case SRB_ELS_CMD_RPT: + case SRB_ELS_CMD_HST: + qla24xx_els_iocb(sp, pkt); + break; + case SRB_CT_CMD: + IS_FWI2_CAPABLE(ha) ? + qla24xx_ct_iocb(sp, pkt) : + qla2x00_ct_iocb(sp, pkt); + break; + case SRB_ADISC_CMD: + IS_FWI2_CAPABLE(ha) ? + qla24xx_adisc_iocb(sp, pkt): + qla2x00_adisc_iocb(sp, pkt); + break; + case SRB_TM_CMD: + qla24xx_tm_iocb(sp, pkt); + break; + default: + break; + } + + wmb(); + qla2x00_start_iocbs(sp->fcport->vha, ha->req_q_map[0]); +done: + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return rval; +} diff --git a/qla2x00t/qla_isr.c b/qla2x00t/qla_isr.c index 233eded41..eccce01db 100644 --- a/qla2x00t/qla_isr.c +++ b/qla2x00t/qla_isr.c @@ -1,24 +1,27 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. - * */ #include "qla_def.h" + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET #include "qla2x_tgt.h" +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ #include #include +#include +#include static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t); -static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t); -static void qla2x00_status_entry(scsi_qla_host_t *, void *); -static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *); -static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *); -static void qla2x00_ms_entry(scsi_qla_host_t *, ms_iocb_entry_t *); -static void qla24xx_ms_entry(scsi_qla_host_t *, struct ct_entry_24xx *); - +static void qla2x00_process_completed_request(struct scsi_qla_host *, + struct req_que *, uint32_t); +static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *); +static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *); +static void qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *, + sts_entry_t *); /** * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200. @@ -32,25 +35,29 @@ static void qla24xx_ms_entry(scsi_qla_host_t *, struct ct_entry_24xx *); irqreturn_t qla2100_intr_handler(int irq, void *dev_id) { - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; struct device_reg_2xxx __iomem *reg; int status; unsigned long iter; uint16_t hccr; uint16_t mb[4]; + struct rsp_que *rsp; unsigned long flags; - ha = (scsi_qla_host_t *) dev_id; - if (!ha) { - printk(KERN_INFO - "%s(): NULL host pointer\n", __func__); + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0x505d, + "%s: NULL response queue pointer.\n", __func__); return (IRQ_NONE); } + ha = rsp->hw; reg = &ha->iobase->isp; status = 0; spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); for (iter = 50; iter--; ) { hccr = RD_REG_WORD(®->hccr); if (hccr & HCCR_RISC_PAUSE) { @@ -59,14 +66,14 @@ qla2100_intr_handler(int irq, void *dev_id) /* * Issue a "HARD" reset in order for the RISC interrupt - * bit to be cleared. Schedule a big hammmer to get + * bit to be cleared. Schedule a big hammer to get * out of the RISC PAUSED state. */ WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); RD_REG_WORD(®->hccr); - ha->isp_ops->fw_dump(ha, 1); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ha->isp_ops->fw_dump(vha, 1); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; } else if ((RD_REG_WORD(®->istatus) & ISR_RISC_INT) == 0) break; @@ -78,24 +85,24 @@ qla2100_intr_handler(int irq, void *dev_id) /* Get mailbox data. */ mb[0] = RD_MAILBOX_REG(ha, reg, 0); if (mb[0] > 0x3fff && mb[0] < 0x8000) { - qla2x00_mbx_completion(ha, mb[0]); + qla2x00_mbx_completion(vha, mb[0]); status |= MBX_INTERRUPT; } else if (mb[0] > 0x7fff && mb[0] < 0xc000) { mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); mb[3] = RD_MAILBOX_REG(ha, reg, 3); - qla2x00_async_event(ha, mb); + qla2x00_async_event(vha, rsp, mb); } else { /*EMPTY*/ - DEBUG2(printk("scsi(%ld): Unrecognized " - "interrupt type (%d).\n", - ha->host_no, mb[0])); + ql_dbg(ql_dbg_async, vha, 0x5025, + "Unrecognized interrupt type (%d).\n", + mb[0]); } /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); RD_REG_WORD(®->semaphore); } else { - qla2x00_process_response_queue(ha); + qla2x00_process_response_queue(rsp); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); @@ -124,50 +131,56 @@ qla2100_intr_handler(int irq, void *dev_id) irqreturn_t qla2300_intr_handler(int irq, void *dev_id) { - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; struct device_reg_2xxx __iomem *reg; int status; unsigned long iter; uint32_t stat; uint16_t hccr; uint16_t mb[4]; + struct rsp_que *rsp; + struct qla_hw_data *ha; unsigned long flags; - ha = (scsi_qla_host_t *) dev_id; - if (!ha) { - printk(KERN_INFO - "%s(): NULL host pointer\n", __func__); + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0x5058, + "%s: NULL response queue pointer.\n", __func__); return (IRQ_NONE); } + ha = rsp->hw; reg = &ha->iobase->isp; status = 0; spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); for (iter = 50; iter--; ) { stat = RD_REG_DWORD(®->u.isp2300.host_status); if (stat & HSR_RISC_PAUSED) { - if (pci_channel_offline(ha->pdev)) + if (unlikely(pci_channel_offline(ha->pdev))) break; hccr = RD_REG_WORD(®->hccr); if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8)) - qla_printk(KERN_INFO, ha, "Parity error -- " - "HCCR=%x, Dumping firmware!\n", hccr); + ql_log(ql_log_warn, vha, 0x5026, + "Parity error -- HCCR=%x, Dumping " + "firmware.\n", hccr); else - qla_printk(KERN_INFO, ha, "RISC paused -- " - "HCCR=%x, Dumping firmware!\n", hccr); + ql_log(ql_log_warn, vha, 0x5027, + "RISC paused -- HCCR=%x, Dumping " + "firmware.\n", hccr); /* * Issue a "HARD" reset in order for the RISC * interrupt bit to be cleared. Schedule a big - * hammmer to get out of the RISC PAUSED state. + * hammer to get out of the RISC PAUSED state. */ WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); RD_REG_WORD(®->hccr); - ha->isp_ops->fw_dump(ha, 1); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ha->isp_ops->fw_dump(vha, 1); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; } else if ((stat & HSR_RISC_INT) == 0) break; @@ -177,7 +190,7 @@ qla2300_intr_handler(int irq, void *dev_id) case 0x2: case 0x10: case 0x11: - qla2x00_mbx_completion(ha, MSW(stat)); + qla2x00_mbx_completion(vha, MSW(stat)); status |= MBX_INTERRUPT; /* Release mailbox registers. */ @@ -188,34 +201,33 @@ qla2300_intr_handler(int irq, void *dev_id) mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); mb[3] = RD_MAILBOX_REG(ha, reg, 3); - qla2x00_async_event(ha, mb); + qla2x00_async_event(vha, rsp, mb); break; case 0x13: - qla2x00_process_response_queue(ha); + qla2x00_process_response_queue(rsp); break; case 0x15: mb[0] = MBA_CMPLT_1_16BIT; mb[1] = MSW(stat); - qla2x00_async_event(ha, mb); + qla2x00_async_event(vha, rsp, mb); break; case 0x16: mb[0] = MBA_SCSI_COMPLETION; mb[1] = MSW(stat); mb[2] = RD_MAILBOX_REG(ha, reg, 2); - qla2x00_async_event(ha, mb); + qla2x00_async_event(vha, rsp, mb); break; #ifdef CONFIG_SCSI_QLA2XXX_TARGET case 0x17: /* FAST_CTIO_COMP */ mb[0] = MBA_CTIO_COMPLETION; mb[1] = MSW(stat); mb[2] = RD_MAILBOX_REG(ha, reg, 2); - qla2x00_async_event(ha, mb); + qla2x00_async_event(vha, rsp, mb); break; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ default: - DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " - "(%d).\n", - ha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x5028, + "Unrecognized interrupt type (%d).\n", stat & 0xff); break; } WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); @@ -238,35 +250,96 @@ qla2300_intr_handler(int irq, void *dev_id) * @mb0: Mailbox0 register */ static void -qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0) +qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) { uint16_t cnt; + uint32_t mboxes; uint16_t __iomem *wptr; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + /* Read all mbox registers? */ + mboxes = (1 << ha->mbx_count) - 1; + if (!ha->mcp) + ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERRROR.\n"); + else + mboxes = ha->mcp->in_mb; + /* Load return mailbox registers. */ ha->flags.mbox_int = 1; ha->mailbox_out[0] = mb0; + mboxes >>= 1; wptr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 1); for (cnt = 1; cnt < ha->mbx_count; cnt++) { if (IS_QLA2200(ha) && cnt == 8) wptr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 8); - if (cnt == 4 || cnt == 5) + if ((cnt == 4 || cnt == 5) && (mboxes & BIT_0)) ha->mailbox_out[cnt] = qla2x00_debounce_register(wptr); - else + else if (mboxes & BIT_0) ha->mailbox_out[cnt] = RD_REG_WORD(wptr); wptr++; + mboxes >>= 1; + } +} + +static void +qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr) +{ + static char *event[] = + { "Complete", "Request Notification", "Time Extension" }; + int rval; + struct device_reg_24xx __iomem *reg24 = &vha->hw->iobase->isp24; + uint16_t __iomem *wptr; + uint16_t cnt, timeout, mb[QLA_IDC_ACK_REGS]; + + /* Seed data -- mailbox1 -> mailbox7. */ + wptr = (uint16_t __iomem *)®24->mailbox1; + for (cnt = 0; cnt < QLA_IDC_ACK_REGS; cnt++, wptr++) + mb[cnt] = RD_REG_WORD(wptr); + + ql_dbg(ql_dbg_async, vha, 0x5021, + "Inter-Driver Communication %s -- " + "%04x %04x %04x %04x %04x %04x %04x.\n", + event[aen & 0xff], mb[0], mb[1], mb[2], mb[3], + mb[4], mb[5], mb[6]); + + /* Acknowledgement needed? [Notify && non-zero timeout]. */ + timeout = (descr >> 8) & 0xf; + if (aen != MBA_IDC_NOTIFY || !timeout) + return; + + ql_dbg(ql_dbg_async, vha, 0x5022, + "%lu Inter-Driver Communication %s -- ACK timeout=%d.\n", + vha->host_no, event[aen & 0xff], timeout); + + rval = qla2x00_post_idc_ack_work(vha, mb); + if (rval != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0x5023, + "IDC failed to post ACK.\n"); +} + +#define LS_UNKNOWN 2 +char * +qla2x00_get_link_speed_str(struct qla_hw_data *ha) +{ + static char *link_speeds[] = {"1", "2", "?", "4", "8", "16", "10"}; + char *link_speed; + int fw_speed = ha->link_data_rate; + + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + link_speed = link_speeds[0]; + else if (fw_speed == 0x13) + link_speed = link_speeds[6]; + else { + link_speed = link_speeds[LS_UNKNOWN]; + if (fw_speed < 6) + link_speed = + link_speeds[fw_speed]; } - if (ha->mcp) { - DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n", - __func__, ha->host_no, ha->mcp->mb[0])); - } else { - DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n", - __func__, ha->host_no)); - } + return link_speed; } /** @@ -275,25 +348,26 @@ qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0) * @mb: Mailbox registers (0 - 3) */ void -qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) +qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) { -#define LS_UNKNOWN 2 - static char *link_speeds[5] = { "1", "2", "?", "4", "8" }; - char *link_speed; uint16_t handle_cnt; - uint16_t cnt; + uint16_t cnt, mbx; uint32_t handles[5]; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; + struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82; uint32_t rscn_entry, host_pid; - uint8_t rscn_queue_index; unsigned long flags; /* Setup to process RIO completion. */ handle_cnt = 0; + if (IS_CNA_CAPABLE(ha)) + goto skip_rio; switch (mb[0]) { #ifdef CONFIG_SCSI_QLA2XXX_TARGET case MBA_CTIO_COMPLETION: -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ case MBA_SCSI_COMPLETION: handles[0] = le32_to_cpu((uint32_t)((mb[2] << 16) | mb[1])); handle_cnt = 1; @@ -344,399 +418,466 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) default: break; } - +skip_rio: switch (mb[0]) { case MBA_SCSI_COMPLETION: /* Fast Post */ - if (!ha->flags.online) + if (!vha->flags.online) break; for (cnt = 0; cnt < handle_cnt; cnt++) - qla2x00_process_completed_request(ha, handles[cnt]); + qla2x00_process_completed_request(vha, rsp->req, + handles[cnt]); break; #ifdef CONFIG_SCSI_QLA2XXX_TARGET case MBA_CTIO_COMPLETION: + handles[0] = le32_to_cpu((uint32_t)((mb[2] << 16) | mb[1])); if (qla_target.tgt2x_ctio_completion) - qla_target.tgt2x_ctio_completion(ha, handles[0]); + qla_target.tgt2x_ctio_completion(vha, handles[0]); break; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ case MBA_RESET: /* Reset */ - DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no)); + ql_dbg(ql_dbg_async, vha, 0x5002, + "Asynchronous RESET.\n"); - set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); + set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); break; case MBA_SYSTEM_ERR: /* System Error */ - qla_printk(KERN_INFO, ha, - "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n", - mb[1], mb[2], mb[3]); + mbx = (IS_QLA81XX(ha) || IS_QLA83XX(ha)) ? + RD_REG_WORD(®24->mailbox7) : 0; + ql_log(ql_log_warn, vha, 0x5003, + "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh " + "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx); - qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]); - ha->isp_ops->fw_dump(ha, 1); + ha->isp_ops->fw_dump(vha, 1); if (IS_FWI2_CAPABLE(ha)) { if (mb[1] == 0 && mb[2] == 0) { - qla_printk(KERN_ERR, ha, + ql_log(ql_log_fatal, vha, 0x5004, "Unrecoverable Hardware Error: adapter " "marked OFFLINE!\n"); - ha->flags.online = 0; - } else - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + vha->flags.online = 0; + vha->device_flags |= DFLG_DEV_FAILED; + } else { + /* Check to see if MPI timeout occurred */ + if ((mbx & MBX_3) && (ha->flags.port0)) + set_bit(MPI_RESET_NEEDED, + &vha->dpc_flags); + + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + } } else if (mb[1] == 0) { - qla_printk(KERN_INFO, ha, + ql_log(ql_log_fatal, vha, 0x5005, "Unrecoverable Hardware Error: adapter marked " "OFFLINE!\n"); - ha->flags.online = 0; + vha->flags.online = 0; + vha->device_flags |= DFLG_DEV_FAILED; } else - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ - DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n"); + ql_log(ql_log_warn, vha, 0x5006, + "ISP Request Transfer Error (%x).\n", mb[1]); - qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ - DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n"); + ql_log(ql_log_warn, vha, 0x5007, + "ISP Response Transfer Error.\n"); - qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET case MBA_ATIO_TRANSFER_ERR: /* ATIO Queue Transfer Error */ - DEBUG2(printk(KERN_INFO "scsi(%ld): ATIO Transfer Error.\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, "ATIO Transfer Error.\n"); + ql_log(ql_log_warn, vha, 0xffff, /* TODO: msg-code */ + "scsi(%ld): ATIO Transfer Error.\n", vha->host_no); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ + case MBA_WAKEUP_THRES: /* Request Queue Wake-up */ + ql_dbg(ql_dbg_async, vha, 0x5008, + "Asynchronous WAKEUP_THRES.\n"); + break; + +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ - DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", ha->host_no, - mb[1])); - qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]); + ql_dbg(ql_dbg_async, vha, 0x5009, + "LIP occurred (%x).\n", mb[1]); - if (atomic_read(&ha->loop_state) != LOOP_DOWN) { - atomic_set(&ha->loop_state, LOOP_DOWN); - atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); - qla2x00_mark_all_devices_lost(ha, 1); + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + qla2x00_mark_all_devices_lost(vha, 1); } - if (ha->parent) { - atomic_set(&ha->vp_state, VP_FAILED); - fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + if (vha->vp_idx) { + atomic_set(&vha->vp_state, VP_FAILED); + fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); } - set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); - set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); + set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); + set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); - ha->flags.management_server_logged_in = 0; - qla2x00_post_aen_work(ha, FCH_EVT_LIP, mb[1]); + vha->flags.management_server_logged_in = 0; + qla2x00_post_aen_work(vha, FCH_EVT_LIP, mb[1]); break; case MBA_LOOP_UP: /* Loop Up Event */ - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - link_speed = link_speeds[0]; + if (IS_QLA2100(ha) || IS_QLA2200(ha)) ha->link_data_rate = PORT_SPEED_1GB; - } else { - link_speed = link_speeds[LS_UNKNOWN]; - if (mb[1] < 5) - link_speed = link_speeds[mb[1]]; + else ha->link_data_rate = mb[1]; - } - DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n", - ha->host_no, link_speed)); - qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n", - link_speed); + ql_dbg(ql_dbg_async, vha, 0x500a, + "LOOP UP detected (%s Gbps).\n", + qla2x00_get_link_speed_str(ha)); - ha->flags.management_server_logged_in = 0; - qla2x00_post_aen_work(ha, FCH_EVT_LINKUP, ha->link_data_rate); + vha->flags.management_server_logged_in = 0; + qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate); break; case MBA_LOOP_DOWN: /* Loop Down Event */ - DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN " - "(%x %x %x).\n", ha->host_no, mb[1], mb[2], mb[3])); - qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n", - mb[1], mb[2], mb[3]); + mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha)) + ? RD_REG_WORD(®24->mailbox4) : 0; + mbx = IS_QLA82XX(ha) ? RD_REG_WORD(®82->mailbox_out[4]) + : mbx; + ql_dbg(ql_dbg_async, vha, 0x500b, + "LOOP DOWN detected (%x %x %x %x).\n", + mb[1], mb[2], mb[3], mbx); - if (atomic_read(&ha->loop_state) != LOOP_DOWN) { - atomic_set(&ha->loop_state, LOOP_DOWN); - atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); - ha->device_flags |= DFLG_NO_CABLE; - qla2x00_mark_all_devices_lost(ha, 1); + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + vha->device_flags |= DFLG_NO_CABLE; + qla2x00_mark_all_devices_lost(vha, 1); } - if (ha->parent) { - atomic_set(&ha->vp_state, VP_FAILED); - fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + if (vha->vp_idx) { + atomic_set(&vha->vp_state, VP_FAILED); + fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); } - ha->flags.management_server_logged_in = 0; + vha->flags.management_server_logged_in = 0; ha->link_data_rate = PORT_SPEED_UNKNOWN; - qla2x00_post_aen_work(ha, FCH_EVT_LINKDOWN, 0); + qla2x00_post_aen_work(vha, FCH_EVT_LINKDOWN, 0); break; case MBA_LIP_RESET: /* LIP reset occurred */ - DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n", - ha->host_no, mb[1])); - qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_async, vha, 0x500c, "LIP reset occurred (%x).\n", mb[1]); - if (atomic_read(&ha->loop_state) != LOOP_DOWN) { - atomic_set(&ha->loop_state, LOOP_DOWN); - atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); - qla2x00_mark_all_devices_lost(ha, 1); + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + qla2x00_mark_all_devices_lost(vha, 1); } - if (ha->parent) { - atomic_set(&ha->vp_state, VP_FAILED); - fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + if (vha->vp_idx) { + atomic_set(&vha->vp_state, VP_FAILED); + fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); } - set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); + set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); ha->operating_mode = LOOP; - ha->flags.management_server_logged_in = 0; - qla2x00_post_aen_work(ha, FCH_EVT_LIPRESET, mb[1]); + vha->flags.management_server_logged_in = 0; + qla2x00_post_aen_work(vha, FCH_EVT_LIPRESET, mb[1]); break; + /* case MBA_DCBX_COMPLETE: */ case MBA_POINT_TO_POINT: /* Point-to-Point */ if (IS_QLA2100(ha)) break; - DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n", - ha->host_no)); + if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA8031(ha)) { + ql_dbg(ql_dbg_async, vha, 0x500d, + "DCBX Completed -- %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + if (ha->notify_dcbx_comp) + complete(&ha->dcbx_comp); + + } else + ql_dbg(ql_dbg_async, vha, 0x500e, + "Asynchronous P2P MODE received.\n"); /* * Until there's a transition from loop down to loop up, treat * this as loop down only. */ - if (atomic_read(&ha->loop_state) != LOOP_DOWN) { - atomic_set(&ha->loop_state, LOOP_DOWN); - if (!atomic_read(&ha->loop_down_timer)) - atomic_set(&ha->loop_down_timer, + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + if (!atomic_read(&vha->loop_down_timer)) + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); - qla2x00_mark_all_devices_lost(ha, 1); + qla2x00_mark_all_devices_lost(vha, 1); } - if (ha->parent) { - atomic_set(&ha->vp_state, VP_FAILED); - fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + if (vha->vp_idx) { + atomic_set(&vha->vp_state, VP_FAILED); + fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); } - if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) { - set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - } - set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); - set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); + if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))) + set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); + + set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); + set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); ha->flags.gpsc_supported = 1; - ha->flags.management_server_logged_in = 0; + vha->flags.management_server_logged_in = 0; break; case MBA_CHG_IN_CONNECTION: /* Change in connection mode */ if (IS_QLA2100(ha)) break; - DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection " - "received.\n", - ha->host_no)); - qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_async, vha, 0x500f, "Configuration change detected: value=%x.\n", mb[1]); - if (atomic_read(&ha->loop_state) != LOOP_DOWN) { - atomic_set(&ha->loop_state, LOOP_DOWN); - if (!atomic_read(&ha->loop_down_timer)) - atomic_set(&ha->loop_down_timer, + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + if (!atomic_read(&vha->loop_down_timer)) + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); - qla2x00_mark_all_devices_lost(ha, 1); + qla2x00_mark_all_devices_lost(vha, 1); } - if (ha->parent) { - atomic_set(&ha->vp_state, VP_FAILED); - fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + if (vha->vp_idx) { + atomic_set(&vha->vp_state, VP_FAILED); + fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); } - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); - set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); break; case MBA_PORT_UPDATE: /* Port database update */ - /* Only handle SCNs for our Vport index. */ - if (ha->parent && ha->vp_idx != (mb[3] & 0xff)) + /* + * Handle only global and vn-port update events + * + * Relevant inputs: + * mb[1] = N_Port handle of changed port + * OR 0xffff for global event + * mb[2] = New login state + * 7 = Port logged out + * mb[3] = LSB is vp_idx, 0xff = all vps + * + * Skip processing if: + * Event is global, vp_idx is NOT all vps, + * vp_idx does not match + * Event is not global, vp_idx does not match + */ + if (IS_QLA2XXX_MIDTYPE(ha) && + ((mb[1] == 0xffff && (mb[3] & 0xff) != 0xff) || + (mb[1] != 0xffff)) && vha->vp_idx != (mb[3] & 0xff)) break; - /* - * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET - * event etc. earlier indicating loop is down) then process - * it. Otherwise ignore it and Wait for RSCN to come in. - */ - atomic_set(&ha->loop_down_timer, 0); - if (atomic_read(&ha->loop_state) != LOOP_DOWN && - atomic_read(&ha->loop_state) != LOOP_DEAD) { - DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE " - "ignored %04x/%04x/%04x.\n", ha->host_no, mb[1], - mb[2], mb[3])); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - DEBUG2(printk(KERN_INFO "scsi(%ld): ha state %d " - "init_done %d oper_mode %d topo %d\n", - ha->host_no, atomic_read(&ha->loop_state), - ha->flags.init_done, ha->operating_mode, - ha->current_topology)); - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif + /* Global event -- port logout or port unavailable. */ + if (mb[1] == 0xffff && mb[2] == 0x7) { + ql_dbg(ql_dbg_async, vha, 0x5010, + "Port unavailable %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + ql_log(ql_log_warn, vha, 0x505e, + "Link is offline.\n"); + + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, + LOOP_DOWN_TIME); + vha->device_flags |= DFLG_NO_CABLE; + qla2x00_mark_all_devices_lost(vha, 1); + } + + if (vha->vp_idx) { + atomic_set(&vha->vp_state, VP_FAILED); + fc_vport_set_state(vha->fc_vport, + FC_VPORT_FAILED); + qla2x00_mark_all_devices_lost(vha, 1); + } + + vha->flags.management_server_logged_in = 0; + ha->link_data_rate = PORT_SPEED_UNKNOWN; break; } - DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n", - ha->host_no)); - DEBUG(printk(KERN_INFO - "scsi(%ld): Port database changed %04x %04x %04x.\n", - ha->host_no, mb[1], mb[2], mb[3])); + /* + * If PORT UPDATE is global (received LIP_OCCURRED/LIP_RESET + * event etc. earlier indicating loop is down) then process + * it. Otherwise ignore it and Wait for RSCN to come in. + */ + atomic_set(&vha->loop_down_timer, 0); + if (atomic_read(&vha->loop_state) != LOOP_DOWN && + atomic_read(&vha->loop_state) != LOOP_DEAD) { + ql_dbg(ql_dbg_async, vha, 0x5011, + "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n", + mb[1], mb[2], mb[3]); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_async_event) + qla_target.tgt_async_event(mb[0], vha, mb); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + break; + } + + ql_dbg(ql_dbg_async, vha, 0x5012, + "Port database changed %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + ql_log(ql_log_warn, vha, 0x505f, + "Link is operational (%s Gbps).\n", + qla2x00_get_link_speed_str(ha)); /* * Mark all devices as missing so we will login again. */ - atomic_set(&ha->loop_state, LOOP_UP); + atomic_set(&vha->loop_state, LOOP_UP); - qla2x00_mark_all_devices_lost(ha, 1); + qla2x00_mark_all_devices_lost(vha, 1); - ha->flags.rscn_queue_overflow = 1; - - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); - set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif + qla_target.tgt_async_event(mb[0], vha, mb); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ break; case MBA_RSCN_UPDATE: /* State Change Registration */ /* Check if the Vport has issued a SCR */ - if (ha->parent && test_bit(VP_SCR_NEEDED, &ha->vp_flags)) + if (vha->vp_idx && test_bit(VP_SCR_NEEDED, &vha->vp_flags)) break; /* Only handle SCNs for our Vport index. */ - if (ha->parent && ha->vp_idx != (mb[3] & 0xff)) + if (ha->flags.npiv_supported && vha->vp_idx != (mb[3] & 0xff)) break; - DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n", - ha->host_no)); - DEBUG(printk(KERN_INFO - "scsi(%ld): RSCN database changed -- %04x %04x %04x.\n", - ha->host_no, mb[1], mb[2], mb[3])); + ql_dbg(ql_dbg_async, vha, 0x5013, + "RSCN database changed -- %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); - rscn_entry = (mb[1] << 16) | mb[2]; - host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) | - ha->d_id.b.al_pa; + rscn_entry = ((mb[1] & 0xff) << 16) | mb[2]; + host_pid = (vha->d_id.b.domain << 16) | (vha->d_id.b.area << 8) + | vha->d_id.b.al_pa; if (rscn_entry == host_pid) { - DEBUG(printk(KERN_INFO - "scsi(%ld): Ignoring RSCN update to local host " - "port ID (%06x)\n", - ha->host_no, host_pid)); + ql_dbg(ql_dbg_async, vha, 0x5014, + "Ignoring RSCN update to local host " + "port ID (%06x).\n", host_pid); break; } - rscn_queue_index = ha->rscn_in_ptr + 1; - if (rscn_queue_index == MAX_RSCN_COUNT) - rscn_queue_index = 0; - if (rscn_queue_index != ha->rscn_out_ptr) { - ha->rscn_queue[ha->rscn_in_ptr] = rscn_entry; - ha->rscn_in_ptr = rscn_queue_index; - } else { - ha->flags.rscn_queue_overflow = 1; - } + /* Ignore reserved bits from RSCN-payload. */ + rscn_entry = ((mb[1] & 0x3ff) << 16) | mb[2]; - atomic_set(&ha->loop_state, LOOP_UPDATE); - atomic_set(&ha->loop_down_timer, 0); - ha->flags.management_server_logged_in = 0; + atomic_set(&vha->loop_down_timer, 0); + vha->flags.management_server_logged_in = 0; - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); - set_bit(RSCN_UPDATE, &ha->dpc_flags); - qla2x00_post_aen_work(ha, FCH_EVT_RSCN, rscn_entry); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(RSCN_UPDATE, &vha->dpc_flags); + qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_async_event) + qla_target.tgt_async_event(mb[0], vha, mb); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ break; /* case MBA_RIO_RESPONSE: */ case MBA_ZIO_RESPONSE: - DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n", - ha->host_no)); - DEBUG(printk(KERN_INFO - "scsi(%ld): [R|Z]IO update completion.\n", - ha->host_no)); + ql_dbg(ql_dbg_async, vha, 0x5015, + "[R|Z]IO update completion.\n"); if (IS_FWI2_CAPABLE(ha)) - qla24xx_process_response_queue(ha); + qla24xx_process_response_queue(vha, rsp); else - qla2x00_process_response_queue(ha); + qla2x00_process_response_queue(rsp); break; case MBA_DISCARD_RND_FRAME: - DEBUG2(printk("scsi(%ld): Discard RND Frame -- %04x %04x " - "%04x.\n", ha->host_no, mb[1], mb[2], mb[3])); + ql_dbg(ql_dbg_async, vha, 0x5016, + "Discard RND Frame -- %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); break; case MBA_TRACE_NOTIFICATION: - DEBUG2(printk("scsi(%ld): Trace Notification -- %04x %04x.\n", - ha->host_no, mb[1], mb[2])); + ql_dbg(ql_dbg_async, vha, 0x5017, + "Trace Notification -- %04x %04x.\n", mb[1], mb[2]); break; case MBA_ISP84XX_ALERT: - DEBUG2(printk("scsi(%ld): ISP84XX Alert Notification -- " - "%04x %04x %04x\n", ha->host_no, mb[1], mb[2], mb[3])); + ql_dbg(ql_dbg_async, vha, 0x5018, + "ISP84XX Alert Notification -- %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); spin_lock_irqsave(&ha->cs84xx->access_lock, flags); switch (mb[1]) { case A84_PANIC_RECOVERY: - qla_printk(KERN_INFO, ha, "Alert 84XX: panic recovery " - "%04x %04x\n", mb[2], mb[3]); + ql_log(ql_log_info, vha, 0x5019, + "Alert 84XX: panic recovery %04x %04x.\n", + mb[2], mb[3]); break; case A84_OP_LOGIN_COMPLETE: ha->cs84xx->op_fw_version = mb[3] << 16 | mb[2]; - DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX:" - "firmware version %x\n", ha->cs84xx->op_fw_version)); + ql_log(ql_log_info, vha, 0x501a, + "Alert 84XX: firmware version %x.\n", + ha->cs84xx->op_fw_version); break; case A84_DIAG_LOGIN_COMPLETE: ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2]; - DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX:" - "diagnostic firmware version %x\n", - ha->cs84xx->diag_fw_version)); + ql_log(ql_log_info, vha, 0x501b, + "Alert 84XX: diagnostic firmware version %x.\n", + ha->cs84xx->diag_fw_version); break; case A84_GOLD_LOGIN_COMPLETE: ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2]; ha->cs84xx->fw_update = 1; - DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX: gold " - "firmware version %x\n", - ha->cs84xx->gold_fw_version)); + ql_log(ql_log_info, vha, 0x501c, + "Alert 84XX: gold firmware version %x.\n", + ha->cs84xx->gold_fw_version); break; default: - qla_printk(KERN_ERR, ha, - "Alert 84xx: Invalid Alert %04x %04x %04x\n", + ql_log(ql_log_warn, vha, 0x501d, + "Alert 84xx: Invalid Alert %04x %04x %04x.\n", mb[1], mb[2], mb[3]); } spin_unlock_irqrestore(&ha->cs84xx->access_lock, flags); break; - + case MBA_DCBX_START: + ql_dbg(ql_dbg_async, vha, 0x501e, + "DCBX Started -- %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + break; + case MBA_DCBX_PARAM_UPDATE: + ql_dbg(ql_dbg_async, vha, 0x501f, + "DCBX Parameters Updated -- %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + break; + case MBA_FCF_CONF_ERR: + ql_dbg(ql_dbg_async, vha, 0x5020, + "FCF Configuration Error -- %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + break; + case MBA_IDC_COMPLETE: + case MBA_IDC_NOTIFY: + case MBA_IDC_TIME_EXT: + qla81xx_idc_event(vha, mb[0], mb[1]); + break; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET case MBA_LOOP_INIT_ERR: printk(KERN_INFO "scsi(%ld): Loop init error received -- " - "%04x %04x %04x.\n", ha->host_no, mb[1], mb[2], + "%04x %04x %04x.\n", vha->host_no, mb[1], mb[2], mb[3]); break; - +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ default: - printk(KERN_INFO "scsi(%ld): Unhandled async event %d " - "received -- %04x %04x %04x.\n", ha->host_no, + ql_dbg(ql_dbg_async, vha, 0x5057, + "Unknown AEN:%04x %04x %04x %04x\n", mb[0], mb[1], mb[2], mb[3]); - break; } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET switch (mb[0]) { case MBA_POINT_TO_POINT: /* Point-to-Point */ case MBA_CHG_IN_CONNECTION: /* Change in connection mode */ @@ -752,74 +893,12 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) case MBA_LOOP_UP: /* Loop Up Event */ case MBA_LOOP_DOWN: /* Loop Down Event */ case MBA_LIP_RESET: /* LIP reset occurred */ -#ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif - break; + qla_target.tgt_async_event(mb[0], vha, mb); } - - if (!ha->parent && ha->num_vhosts) - qla2x00_alert_all_vps(ha, mb); -} - -static void -qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data) -{ - fc_port_t *fcport = data; - - if (fcport->ha->max_q_depth <= sdev->queue_depth) - return; - - if (sdev->ordered_tags) - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, - sdev->queue_depth + 1); - else - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, - sdev->queue_depth + 1); - - fcport->last_ramp_up = jiffies; - - DEBUG2(qla_printk(KERN_INFO, fcport->ha, - "scsi(%ld:%d:%d:%lld): Queue depth adjusted-up to %d.\n", - fcport->ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun, - sdev->queue_depth)); -} - -static void -qla2x00_adjust_sdev_qdepth_down(struct scsi_device *sdev, void *data) -{ - fc_port_t *fcport = data; - - if (!scsi_track_queue_full(sdev, sdev->queue_depth - 1)) - return; - - DEBUG2(qla_printk(KERN_INFO, fcport->ha, - "scsi(%ld:%d:%d:%lld): Queue depth adjusted-down to %d.\n", - fcport->ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun, - sdev->queue_depth)); -} - -static inline void -qla2x00_ramp_up_queue_depth(scsi_qla_host_t *ha, srb_t *sp) -{ - fc_port_t *fcport; - struct scsi_device *sdev; - - sdev = sp->cmd->device; - if (sdev->queue_depth >= ha->max_q_depth) - return; - - fcport = sp->fcport; - if (time_before(jiffies, - fcport->last_ramp_up + ql2xqfullrampup * HZ)) - return; - if (time_before(jiffies, - fcport->last_queue_full + ql2xqfullrampup * HZ)) - return; - - starget_for_each_device(sdev->sdev_target, fcport, - qla2x00_adjust_sdev_qdepth_up); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + if (!vha->vp_idx && ha->num_vhosts) + qla2x00_alert_all_vps(rsp, mb); } /** @@ -828,87 +907,493 @@ qla2x00_ramp_up_queue_depth(scsi_qla_host_t *ha, srb_t *sp) * @index: SRB index */ static void -qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index) +qla2x00_process_completed_request(struct scsi_qla_host *vha, + struct req_que *req, uint32_t index) { srb_t *sp; + struct qla_hw_data *ha = vha->hw; #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (HANDLE_IS_CTIO_COMP(index)) { if (qla_target.tgt2x_ctio_completion) - qla_target.tgt2x_ctio_completion(ha, index); + qla_target.tgt2x_ctio_completion(vha, index); return; } -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ /* Validate handle. */ if (index >= MAX_OUTSTANDING_COMMANDS) { - DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n", - ha->host_no, index)); - qla_printk(KERN_WARNING, ha, - "Invalid SCSI completion handle %d.\n", index); - DEBUG2(dump_stack()); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ql_log(ql_log_warn, vha, 0x3014, + "Invalid SCSI command index (%x).\n", index); + + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); return; } - sp = ha->outstanding_cmds[index]; + sp = req->outstanding_cmds[index]; if (sp) { /* Free outstanding command slot. */ - ha->outstanding_cmds[index] = NULL; - - CMD_COMPL_STATUS(sp->cmd) = 0L; - CMD_SCSI_STATUS(sp->cmd) = 0L; + req->outstanding_cmds[index] = NULL; /* Save ISP completion status */ - sp->cmd->result = DID_OK << 16; - - qla2x00_ramp_up_queue_depth(ha, sp); - qla2x00_sp_compl(ha, sp); + sp->done(ha, sp, DID_OK << 16); } else { - DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, - "Invalid ISP SCSI completion handle\n"); - DEBUG2(dump_stack()); + ql_log(ql_log_warn, vha, 0x3016, "Invalid SCSI SRB.\n"); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); } } +static srb_t * +qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func, + struct req_que *req, void *iocb) +{ + struct qla_hw_data *ha = vha->hw; + sts_entry_t *pkt = iocb; + srb_t *sp = NULL; + uint16_t index; + + index = LSW(pkt->handle); + if (index >= MAX_OUTSTANDING_COMMANDS) { + ql_log(ql_log_warn, vha, 0x5031, + "Invalid command index (%x).\n", index); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + goto done; + } + sp = req->outstanding_cmds[index]; + if (!sp) { + ql_log(ql_log_warn, vha, 0x5032, + "Invalid completion handle (%x) -- timed-out.\n", index); + return sp; + } + if (sp->handle != index) { + ql_log(ql_log_warn, vha, 0x5033, + "SRB handle (%x) mismatch %x.\n", sp->handle, index); + return NULL; + } + + req->outstanding_cmds[index] = NULL; + +done: + return sp; +} + +static void +qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct mbx_entry *mbx) +{ + const char func[] = "MBX-IOCB"; + const char *type; + fc_port_t *fcport; + srb_t *sp; + struct srb_iocb *lio; + uint16_t *data; + uint16_t status; + + sp = qla2x00_get_sp_from_handle(vha, func, req, mbx); + if (!sp) + return; + + lio = &sp->u.iocb_cmd; + type = sp->name; + fcport = sp->fcport; + data = lio->u.logio.data; + + data[0] = MBS_COMMAND_ERROR; + data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? + QLA_LOGIO_LOGIN_RETRIED: 0; + if (mbx->entry_status) { + ql_dbg(ql_dbg_async, vha, 0x5043, + "Async-%s error entry - hdl=%x portid=%02x%02x%02x " + "entry-status=%x status=%x state-flag=%x " + "status-flags=%x.\n", type, sp->handle, + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa, mbx->entry_status, + le16_to_cpu(mbx->status), le16_to_cpu(mbx->state_flags), + le16_to_cpu(mbx->status_flags)); + + ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5029, + (uint8_t *)mbx, sizeof(*mbx)); + + goto logio_done; + } + + status = le16_to_cpu(mbx->status); + if (status == 0x30 && sp->type == SRB_LOGIN_CMD && + le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) + status = 0; + if (!status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) { + ql_dbg(ql_dbg_async, vha, 0x5045, + "Async-%s complete - hdl=%x portid=%02x%02x%02x mbx1=%x.\n", + type, sp->handle, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + le16_to_cpu(mbx->mb1)); + + data[0] = MBS_COMMAND_COMPLETE; + if (sp->type == SRB_LOGIN_CMD) { + fcport->port_type = FCT_TARGET; + if (le16_to_cpu(mbx->mb1) & BIT_0) + fcport->port_type = FCT_INITIATOR; + else if (le16_to_cpu(mbx->mb1) & BIT_1) + fcport->flags |= FCF_FCP2_DEVICE; + } + goto logio_done; + } + + data[0] = le16_to_cpu(mbx->mb0); + switch (data[0]) { + case MBS_PORT_ID_USED: + data[1] = le16_to_cpu(mbx->mb1); + break; + case MBS_LOOP_ID_USED: + break; + default: + data[0] = MBS_COMMAND_ERROR; + break; + } + + ql_log(ql_log_warn, vha, 0x5046, + "Async-%s failed - hdl=%x portid=%02x%02x%02x status=%x " + "mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n", type, sp->handle, + fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, + status, le16_to_cpu(mbx->mb0), le16_to_cpu(mbx->mb1), + le16_to_cpu(mbx->mb2), le16_to_cpu(mbx->mb6), + le16_to_cpu(mbx->mb7)); + +logio_done: + sp->done(vha, sp, 0); +} + +static void +qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, + sts_entry_t *pkt, int iocb_type) +{ + const char func[] = "CT_IOCB"; + const char *type; + srb_t *sp; + struct fc_bsg_job *bsg_job; + uint16_t comp_status; + int res; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + + bsg_job = sp->u.bsg_job; + + type = "ct pass-through"; + + comp_status = le16_to_cpu(pkt->comp_status); + + /* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT + * fc payload to the caller + */ + bsg_job->reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + + if (comp_status != CS_COMPLETE) { + if (comp_status == CS_DATA_UNDERRUN) { + res = DID_OK << 16; + bsg_job->reply->reply_payload_rcv_len = + le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len); + + ql_log(ql_log_warn, vha, 0x5048, + "CT pass-through-%s error " + "comp_status-status=0x%x total_byte = 0x%x.\n", + type, comp_status, + bsg_job->reply->reply_payload_rcv_len); + } else { + ql_log(ql_log_warn, vha, 0x5049, + "CT pass-through-%s error " + "comp_status-status=0x%x.\n", type, comp_status); + res = DID_ERROR << 16; + bsg_job->reply->reply_payload_rcv_len = 0; + } + ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035, + (uint8_t *)pkt, sizeof(*pkt)); + } else { + res = DID_OK << 16; + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + bsg_job->reply_len = 0; + } + sp->done(vha, sp, res); +} + +static void +qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, + struct sts_entry_24xx *pkt, int iocb_type) +{ + const char func[] = "ELS_CT_IOCB"; + const char *type; + srb_t *sp; + struct fc_bsg_job *bsg_job; + uint16_t comp_status; + uint32_t fw_status[3]; + uint8_t* fw_sts_ptr; + int res; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + bsg_job = sp->u.bsg_job; + + type = NULL; + switch (sp->type) { + case SRB_ELS_CMD_RPT: + case SRB_ELS_CMD_HST: + type = "els"; + break; + case SRB_CT_CMD: + type = "ct pass-through"; + break; + default: + ql_log(ql_log_warn, vha, 0x503e, + "Unrecognized SRB: (%p) type=%d.\n", sp, sp->type); + return; + } + + comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status); + fw_status[1] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_1); + fw_status[2] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_2); + + /* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT + * fc payload to the caller + */ + bsg_job->reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; + bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(fw_status); + + if (comp_status != CS_COMPLETE) { + if (comp_status == CS_DATA_UNDERRUN) { + res = DID_OK << 16; + bsg_job->reply->reply_payload_rcv_len = + le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count); + + ql_dbg(ql_dbg_user, vha, 0x503f, + "ELS-CT pass-through-%s error hdl=%x comp_status-status=0x%x " + "error subcode 1=0x%x error subcode 2=0x%x total_byte = 0x%x.\n", + type, sp->handle, comp_status, fw_status[1], fw_status[2], + le16_to_cpu(((struct els_sts_entry_24xx *) + pkt)->total_byte_count)); + fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply); + memcpy( fw_sts_ptr, fw_status, sizeof(fw_status)); + } + else { + ql_dbg(ql_dbg_user, vha, 0x5040, + "ELS-CT pass-through-%s error hdl=%x comp_status-status=0x%x " + "error subcode 1=0x%x error subcode 2=0x%x.\n", + type, sp->handle, comp_status, + le16_to_cpu(((struct els_sts_entry_24xx *) + pkt)->error_subcode_1), + le16_to_cpu(((struct els_sts_entry_24xx *) + pkt)->error_subcode_2)); + res = DID_ERROR << 16; + bsg_job->reply->reply_payload_rcv_len = 0; + fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply); + memcpy( fw_sts_ptr, fw_status, sizeof(fw_status)); + } + ql_dump_buffer(ql_dbg_user + ql_dbg_buffer, vha, 0x5056, + (uint8_t *)pkt, sizeof(*pkt)); + } + else { + res = DID_OK << 16; + bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len; + bsg_job->reply_len = 0; + } + sp->done(vha, sp, 0); +} + +static void +qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, + struct logio_entry_24xx *logio) +{ + const char func[] = "LOGIO-IOCB"; + const char *type; + fc_port_t *fcport; + srb_t *sp; + struct srb_iocb *lio; + uint16_t *data; + uint32_t iop[2]; + + sp = qla2x00_get_sp_from_handle(vha, func, req, logio); + if (!sp) + return; + lio = &sp->u.iocb_cmd; + type = sp->name; + + fcport = sp->fcport; + data = lio->u.logio.data; + + data[0] = MBS_COMMAND_ERROR; + data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? + QLA_LOGIO_LOGIN_RETRIED: 0; + if (logio->entry_status) { + ql_log(ql_log_warn, fcport->vha, 0x5034, + "Async-%s error entry - hdl=%x" + "portid=%02x%02x%02x entry-status=%x.\n", + type, sp->handle, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + logio->entry_status); + ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x504d, + (uint8_t *)logio, sizeof(*logio)); + + goto logio_done; + } + + if (le16_to_cpu(logio->comp_status) == CS_COMPLETE) { + ql_dbg(ql_dbg_async, fcport->vha, 0x5036, + "Async-%s complete - hdl=%x portid=%02x%02x%02x " + "iop0=%x.\n", type, sp->handle, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + le32_to_cpu(logio->io_parameter[0])); + + data[0] = MBS_COMMAND_COMPLETE; + if (sp->type != SRB_LOGIN_CMD) + goto logio_done; + + iop[0] = le32_to_cpu(logio->io_parameter[0]); + if (iop[0] & BIT_4) { + fcport->port_type = FCT_TARGET; + if (iop[0] & BIT_8) + fcport->flags |= FCF_FCP2_DEVICE; + } else if (iop[0] & BIT_5) + fcport->port_type = FCT_INITIATOR; + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (iop[0] & BIT_7) + fcport->conf_compl_supported = 1; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + + if (logio->io_parameter[7] || logio->io_parameter[8]) + fcport->supported_classes |= FC_COS_CLASS2; + if (logio->io_parameter[9] || logio->io_parameter[10]) + fcport->supported_classes |= FC_COS_CLASS3; + + goto logio_done; + } + + iop[0] = le32_to_cpu(logio->io_parameter[0]); + iop[1] = le32_to_cpu(logio->io_parameter[1]); + switch (iop[0]) { + case LSC_SCODE_PORTID_USED: + data[0] = MBS_PORT_ID_USED; + data[1] = LSW(iop[1]); + break; + case LSC_SCODE_NPORT_USED: + data[0] = MBS_LOOP_ID_USED; + break; + default: + data[0] = MBS_COMMAND_ERROR; + break; + } + + ql_dbg(ql_dbg_async, fcport->vha, 0x5037, + "Async-%s failed - hdl=%x portid=%02x%02x%02x comp=%x " + "iop0=%x iop1=%x.\n", type, sp->handle, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + le16_to_cpu(logio->comp_status), + le32_to_cpu(logio->io_parameter[0]), + le32_to_cpu(logio->io_parameter[1])); + +logio_done: + sp->done(vha, sp, 0); +} + +static void +qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct tsk_mgmt_entry *tsk) +{ + const char func[] = "TMF-IOCB"; + const char *type; + fc_port_t *fcport; + srb_t *sp; + struct srb_iocb *iocb; + struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk; + int error = 1; + + sp = qla2x00_get_sp_from_handle(vha, func, req, tsk); + if (!sp) + return; + + iocb = &sp->u.iocb_cmd; + type = sp->name; + fcport = sp->fcport; + + if (sts->entry_status) { + ql_log(ql_log_warn, fcport->vha, 0x5038, + "Async-%s error - hdl=%x entry-status(%x).\n", + type, sp->handle, sts->entry_status); + } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + ql_log(ql_log_warn, fcport->vha, 0x5039, + "Async-%s error - hdl=%x completion status(%x).\n", + type, sp->handle, sts->comp_status); + } else if (!(le16_to_cpu(sts->scsi_status) & + SS_RESPONSE_INFO_LEN_VALID)) { + ql_log(ql_log_warn, fcport->vha, 0x503a, + "Async-%s error - hdl=%x no response info(%x).\n", + type, sp->handle, sts->scsi_status); + } else if (le32_to_cpu(sts->rsp_data_len) < 4) { + ql_log(ql_log_warn, fcport->vha, 0x503b, + "Async-%s error - hdl=%x not enough response(%d).\n", + type, sp->handle, sts->rsp_data_len); + } else if (sts->data[3]) { + ql_log(ql_log_warn, fcport->vha, 0x503c, + "Async-%s error - hdl=%x response(%x).\n", + type, sp->handle, sts->data[3]); + } else { + error = 0; + } + + if (error) { + iocb->u.tmf.data = error; + ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5055, + (uint8_t *)sts, sizeof(*sts)); + } + + sp->done(vha, sp, 0); +} + /** * qla2x00_process_response_queue() - Process response queue entries. * @ha: SCSI driver HA context */ void -qla2x00_process_response_queue(struct scsi_qla_host *ha) +qla2x00_process_response_queue(struct rsp_que *rsp) { + struct scsi_qla_host *vha; + struct qla_hw_data *ha = rsp->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; sts_entry_t *pkt; uint16_t handle_cnt; uint16_t cnt; - if (!ha->flags.online) + vha = pci_get_drvdata(ha->pdev); + + if (!vha->flags.online) return; - while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) { - pkt = (sts_entry_t *)ha->response_ring_ptr; + while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) { + pkt = (sts_entry_t *)rsp->ring_ptr; - DEBUG5(printk(KERN_INFO "%s(): IOCB data:\n", __func__)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)pkt, RESPONSE_ENTRY_SIZE)); - - ha->rsp_ring_index++; - if (ha->rsp_ring_index == ha->response_q_length) { - ha->rsp_ring_index = 0; - ha->response_ring_ptr = ha->response_ring; + rsp->ring_index++; + if (rsp->ring_index == rsp->length) { + rsp->ring_index = 0; + rsp->ring_ptr = rsp->ring; } else { - ha->response_ring_ptr++; + rsp->ring_ptr++; } if (pkt->entry_status != 0) { - DEBUG3(printk(KERN_INFO - "scsi(%ld): Process error entry.\n", ha->host_no)); - - qla2x00_error_entry(ha, pkt); + qla2x00_error_entry(vha, rsp, pkt); #ifdef CONFIG_SCSI_QLA2XXX_TARGET switch (pkt->entry_type) { case ACCEPT_TGT_IO_TYPE: @@ -924,7 +1409,11 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha) wmb(); continue; } -#endif +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ + ((response_t *)pkt)->signature = RESPONSE_PROCESSED; + wmb(); + continue; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ } switch (pkt->entry_type) { @@ -936,60 +1425,46 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha) case NOTIFY_ACK_TYPE: case ENABLE_LUN_TYPE: case MODIFY_LUN_TYPE: - DEBUG5(printk(KERN_WARNING - "qla2x00_response_pkt: " - "calling tgt_response_pkt %p " - "(type %02X)\n", - qla_target.tgt_response_pkt, - pkt->entry_type);); if (qla_target.tgt_response_pkt) - qla_target.tgt_response_pkt(ha, + qla_target.tgt_response_pkt(vha, (response_t *)pkt); break; #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ case STATUS_TYPE: - qla2x00_status_entry(ha, pkt); + qla2x00_status_entry(vha, rsp, pkt); break; case STATUS_TYPE_21: handle_cnt = ((sts21_entry_t *)pkt)->handle_count; for (cnt = 0; cnt < handle_cnt; cnt++) { - qla2x00_process_completed_request(ha, + qla2x00_process_completed_request(vha, rsp->req, ((sts21_entry_t *)pkt)->handle[cnt]); } break; case STATUS_TYPE_22: handle_cnt = ((sts22_entry_t *)pkt)->handle_count; for (cnt = 0; cnt < handle_cnt; cnt++) { - qla2x00_process_completed_request(ha, + qla2x00_process_completed_request(vha, rsp->req, ((sts22_entry_t *)pkt)->handle[cnt]); } break; case STATUS_CONT_TYPE: - qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt); + qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt); + break; + case MBX_IOCB_TYPE: + qla2x00_mbx_iocb_entry(vha, rsp->req, + (struct mbx_entry *)pkt); + break; + case CT_IOCB_TYPE: + qla2x00_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE); break; case MARKER_TYPE: break; - case MS_IOCB_TYPE: - if (ha->outstanding_cmds[pkt->handle]) - qla2x00_ms_entry(ha, (ms_iocb_entry_t *)pkt); - else { - if (ha->pass_thru_cmd_result) - DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru cmd result on.\n")); - if (!ha->pass_thru_cmd_in_process) - DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru in process off.\n")); - - ha->pass_thru_cmd_result = 1; - complete(&ha->pass_thru_intr_comp); - } - break; default: /* Type Not Supported. */ - DEBUG4(printk(KERN_WARNING - "scsi(%ld): Received unknown response pkt type %x " + ql_log(ql_log_warn, vha, 0x504a, + "Received unknown response pkt type %x " "entry status=%x.\n", - ha->host_no, pkt->entry_type, pkt->entry_status)); + pkt->entry_type, pkt->entry_status); break; } ((response_t *)pkt)->signature = RESPONSE_PROCESSED; @@ -997,36 +1472,180 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha) } /* Adjust ring index */ - WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), ha->rsp_ring_index); + WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), rsp->ring_index); } static inline void -qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len) +qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, + uint32_t sense_len, struct rsp_que *rsp, int res) { - struct scsi_cmnd *cp = sp->cmd; + struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_cmnd *cp = GET_CMD_SP(sp); + uint32_t track_sense_len; if (sense_len >= SCSI_SENSE_BUFFERSIZE) sense_len = SCSI_SENSE_BUFFERSIZE; - CMD_ACTUAL_SNSLEN(cp) = sense_len; - sp->request_sense_length = sense_len; - sp->request_sense_ptr = cp->sense_buffer; - if (sp->request_sense_length > 32) - sense_len = 32; + SET_CMD_SENSE_LEN(sp, sense_len); + SET_CMD_SENSE_PTR(sp, cp->sense_buffer); + track_sense_len = sense_len; + + if (sense_len > par_sense_len) + sense_len = par_sense_len; memcpy(cp->sense_buffer, sense_data, sense_len); - sp->request_sense_ptr += sense_len; - sp->request_sense_length -= sense_len; - if (sp->request_sense_length != 0) - sp->ha->status_srb = sp; + SET_CMD_SENSE_PTR(sp, cp->sense_buffer + sense_len); + track_sense_len -= sense_len; + SET_CMD_SENSE_LEN(sp, track_sense_len); - DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) " - "cmd=%p pid=%ld\n", __func__, sp->ha->host_no, cp->device->channel, - cp->device->id, cp->device->lun, cp, cp->serial_number)); - if (sense_len) - DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, - CMD_ACTUAL_SNSLEN(cp))); + if (track_sense_len != 0) { + rsp->status_srb = sp; + cp->result = res; + } + + if (sense_len) { + ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x301c, + "Check condition Sense data, nexus%ld:%d:%lld cmd=%p.\n", + sp->fcport->vha->host_no, cp->device->id, (u64)cp->device->lun, + cp); + ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302b, + cp->sense_buffer, sense_len); + } +} + +struct scsi_dif_tuple { + __be16 guard; /* Checksum */ + __be16 app_tag; /* APPL identifer */ + __be32 ref_tag; /* Target LBA or indirect LBA */ +}; + +/* + * Checks the guard or meta-data for the type of error + * detected by the HBA. In case of errors, we set the + * ASC/ASCQ fields in the sense buffer with ILLEGAL_REQUEST + * to indicate to the kernel that the HBA detected error. + */ +static inline int +qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) +{ + struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + uint8_t *ap = &sts24->data[12]; + uint8_t *ep = &sts24->data[20]; + uint32_t e_ref_tag, a_ref_tag; + uint16_t e_app_tag, a_app_tag; + uint16_t e_guard, a_guard; + + /* + * swab32 of the "data" field in the beginning of qla2x00_status_entry() + * would make guard field appear at offset 2 + */ + a_guard = le16_to_cpu(*(uint16_t *)(ap + 2)); + a_app_tag = le16_to_cpu(*(uint16_t *)(ap + 0)); + a_ref_tag = le32_to_cpu(*(uint32_t *)(ap + 4)); + e_guard = le16_to_cpu(*(uint16_t *)(ep + 2)); + e_app_tag = le16_to_cpu(*(uint16_t *)(ep + 0)); + e_ref_tag = le32_to_cpu(*(uint32_t *)(ep + 4)); + + ql_dbg(ql_dbg_io, vha, 0x3023, + "iocb(s) %p Returned STATUS.\n", sts24); + + ql_dbg(ql_dbg_io, vha, 0x3024, + "DIF ERROR in cmd 0x%x lba 0x%llx act ref" + " tag=0x%x, exp ref_tag=0x%x, act app tag=0x%x, exp app" + " tag=0x%x, act guard=0x%x, exp guard=0x%x.\n", + cmd->cmnd[0], (u64)scsi_get_lba(cmd), a_ref_tag, e_ref_tag, + a_app_tag, e_app_tag, a_guard, e_guard); + + /* + * Ignore sector if: + * For type 3: ref & app tag is all 'f's + * For type 0,1,2: app tag is all 'f's + */ + if ((a_app_tag == 0xffff) && + ((scsi_get_prot_type(cmd) != SCSI_PROT_DIF_TYPE3) || + (a_ref_tag == 0xffffffff))) { + uint32_t blocks_done, resid; + sector_t lba_s = scsi_get_lba(cmd); + + /* 2TB boundary case covered automatically with this */ + blocks_done = e_ref_tag - (uint32_t)lba_s + 1; + + resid = scsi_bufflen(cmd) - (blocks_done * + cmd->device->sector_size); + + scsi_set_resid(cmd, resid); + cmd->result = DID_OK << 16; + + /* Update protection tag */ + if (scsi_prot_sg_count(cmd)) { + uint32_t i, j = 0, k = 0, num_ent; + struct scatterlist *sg; + struct sd_dif_tuple *spt; + + /* Patch the corresponding protection tags */ + scsi_for_each_prot_sg(cmd, sg, + scsi_prot_sg_count(cmd), i) { + num_ent = sg_dma_len(sg) / 8; + if (k + num_ent < blocks_done) { + k += num_ent; + continue; + } + j = blocks_done - k - 1; + k = blocks_done; + break; + } + + if (k != blocks_done) { + ql_log(ql_log_warn, vha, 0x302f, + "unexpected tag values tag:lba=%x:%llx)\n", + e_ref_tag, (unsigned long long)lba_s); + return 1; + } + + spt = page_address(sg_page(sg)) + sg->offset; + spt += j; + + spt->app_tag = 0xffff; + if (scsi_get_prot_type(cmd) == SCSI_PROT_DIF_TYPE3) + spt->ref_tag = 0xffffffff; + } + + return 0; + } + + /* check guard */ + if (e_guard != a_guard) { + scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, + 0x10, 0x1); + set_driver_byte(cmd, DRIVER_SENSE); + set_host_byte(cmd, DID_ABORT); + cmd->result |= SAM_STAT_CHECK_CONDITION << 1; + return 1; + } + + /* check ref tag */ + if (e_ref_tag != a_ref_tag) { + scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, + 0x10, 0x3); + set_driver_byte(cmd, DRIVER_SENSE); + set_host_byte(cmd, DID_ABORT); + cmd->result |= SAM_STAT_CHECK_CONDITION << 1; + return 1; + } + + /* check appl tag */ + if (e_app_tag != a_app_tag) { + scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, + 0x10, 0x2); + set_driver_byte(cmd, DRIVER_SENSE); + set_host_byte(cmd, DID_ABORT); + cmd->result |= SAM_STAT_CHECK_CONDITION << 1; + return 1; + } + + return 1; } /** @@ -1035,7 +1654,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len) * @pkt: Entry pointer */ static void -qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) +qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) { srb_t *sp; fc_port_t *fcport; @@ -1044,10 +1663,18 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) struct sts_entry_24xx *sts24; uint16_t comp_status; uint16_t scsi_status; + uint16_t ox_id; uint8_t lscsi_status; int32_t resid; - uint32_t sense_len, rsp_info_len, resid_len, fw_resid_len; + uint32_t sense_len, par_sense_len, rsp_info_len, resid_len, + fw_resid_len; uint8_t *rsp_info, *sense_data; + struct qla_hw_data *ha = vha->hw; + uint32_t handle; + uint16_t que; + struct req_que *req; + int logit = 1; + int res = 0; sts = (sts_entry_t *) pkt; sts24 = (struct sts_entry_24xx *) pkt; @@ -1058,99 +1685,90 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) comp_status = le16_to_cpu(sts->comp_status); scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK; } + handle = (uint32_t) LSW(sts->handle); + que = MSW(sts->handle); + req = ha->req_q_map[que]; /* Fast path completion. */ if (comp_status == CS_COMPLETE && scsi_status == 0) { - DEBUG3(printk(KERN_WARNING "scsi(%ld): " - "Status Type %#x:\n" - " entry_count\t%d\n" - " entry_status\t%#x\n" - " handle:\t%#x\n" - " scsi_status:\t%#x\n" - " comp_status:\t%#x\n" - " state_flags:\t%#x\n" - " status_flags:\t%#x\n", - ha->host_no, - sts->entry_type, - sts->entry_count, - sts->entry_status, - sts->handle, - sts->scsi_status, - sts->comp_status, - sts->state_flags, - sts->status_flags)); - qla2x00_process_completed_request(ha, sts->handle); + qla2x00_process_completed_request(vha, req, handle); return; } /* Validate handle. */ - if (sts->handle < MAX_OUTSTANDING_COMMANDS) { - sp = ha->outstanding_cmds[sts->handle]; - ha->outstanding_cmds[sts->handle] = NULL; + if (handle < MAX_OUTSTANDING_COMMANDS) { + sp = req->outstanding_cmds[handle]; + req->outstanding_cmds[handle] = NULL; } else sp = NULL; if (sp == NULL) { - DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n"); + ql_dbg(ql_dbg_io, vha, 0x3017, + "Invalid status handle (0x%x).\n", sts->handle); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); return; } - cp = sp->cmd; + cp = GET_CMD_SP(sp); if (cp == NULL) { - DEBUG2(printk("scsi(%ld): Command already returned back to OS " - "pkt->handle=%d sp=%p.\n", ha->host_no, sts->handle, sp)); - qla_printk(KERN_WARNING, ha, - "Command is NULL: already returned to OS (sp=%p)\n", sp); + ql_dbg(ql_dbg_io, vha, 0x3018, + "Command already returned (0x%x/%p).\n", + sts->handle, sp); return; } - lscsi_status = scsi_status & STATUS_MASK; - CMD_ENTRY_STATUS(cp) = sts->entry_status; - CMD_COMPL_STATUS(cp) = comp_status; - CMD_SCSI_STATUS(cp) = scsi_status; + lscsi_status = scsi_status & STATUS_MASK; fcport = sp->fcport; - sense_len = rsp_info_len = resid_len = fw_resid_len = 0; + ox_id = 0; + sense_len = par_sense_len = rsp_info_len = resid_len = + fw_resid_len = 0; if (IS_FWI2_CAPABLE(ha)) { - sense_len = le32_to_cpu(sts24->sense_len); - rsp_info_len = le32_to_cpu(sts24->rsp_data_len); - resid_len = le32_to_cpu(sts24->rsp_residual_count); - fw_resid_len = le32_to_cpu(sts24->residual_len); + if (scsi_status & SS_SENSE_LEN_VALID) + sense_len = le32_to_cpu(sts24->sense_len); + if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) + rsp_info_len = le32_to_cpu(sts24->rsp_data_len); + if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) + resid_len = le32_to_cpu(sts24->rsp_residual_count); + if (comp_status == CS_DATA_UNDERRUN) + fw_resid_len = le32_to_cpu(sts24->residual_len); rsp_info = sts24->data; sense_data = sts24->data; host_to_fcp_swap(sts24->data, sizeof(sts24->data)); + ox_id = le16_to_cpu(sts24->ox_id); + par_sense_len = sizeof(sts24->data); } else { - sense_len = le16_to_cpu(sts->req_sense_length); - rsp_info_len = le16_to_cpu(sts->rsp_info_len); + if (scsi_status & SS_SENSE_LEN_VALID) + sense_len = le16_to_cpu(sts->req_sense_length); + if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) + rsp_info_len = le16_to_cpu(sts->rsp_info_len); resid_len = le32_to_cpu(sts->residual_length); rsp_info = sts->rsp_info; sense_data = sts->req_sense_data; + par_sense_len = sizeof(sts->req_sense_data); } /* Check for any FCP transport errors. */ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) { /* Sense data lies beyond any FCP RESPONSE data. */ - if (IS_FWI2_CAPABLE(ha)) + if (IS_FWI2_CAPABLE(ha)) { sense_data += rsp_info_len; + par_sense_len -= rsp_info_len; + } if (rsp_info_len > 3 && rsp_info[3]) { - DEBUG2(printk("scsi(%ld:%d:%d:%lld) FCP I/O protocol " - "failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..." - "retrying command\n", ha->host_no, - cp->device->channel, cp->device->id, - (unsigned long long)cp->device->lun, rsp_info_len, rsp_info[0], - rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4], - rsp_info[5], rsp_info[6], rsp_info[7])); + ql_dbg(ql_dbg_io, fcport->vha, 0x3019, + "FCP I/O protocol failure (0x%x/0x%x).\n", + rsp_info_len, rsp_info[3]); - cp->result = DID_BUS_BUSY << 16; - qla2x00_sp_compl(ha, sp); - return; + res = DID_BUS_BUSY << 16; + goto out; } } @@ -1166,43 +1784,33 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) case CS_COMPLETE: case CS_QUEUE_FULL: if (scsi_status == 0) { - cp->result = DID_OK << 16; + res = DID_OK << 16; break; } if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) { resid = resid_len; scsi_set_resid(cp, resid); - CMD_RESID_LEN(cp) = resid; if (!lscsi_status && ((unsigned)(scsi_bufflen(cp) - resid) < cp->underflow)) { - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%lld): Mid-layer underflow " - "detected (%x of %x bytes)...returning " - "error status.\n", ha->host_no, - cp->device->channel, cp->device->id, - (unsigned long long)cp->device->lun, resid, - scsi_bufflen(cp)); + ql_dbg(ql_dbg_io, fcport->vha, 0x301a, + "Mid-layer underflow " + "detected (0x%x of 0x%x bytes).\n", + resid, scsi_bufflen(cp)); - cp->result = DID_ERROR << 16; + res = DID_ERROR << 16; break; } } - cp->result = DID_OK << 16 | lscsi_status; + res = DID_OK << 16 | lscsi_status; if (lscsi_status == SAM_STAT_TASK_SET_FULL) { - DEBUG2(printk(KERN_INFO - "scsi(%ld): QUEUE FULL status detected " - "0x%x-0x%x.\n", ha->host_no, comp_status, - scsi_status)); - - /* Adjust queue depth for all luns on the port. */ - fcport->last_queue_full = jiffies; - starget_for_each_device(cp->device->sdev_target, - fcport, qla2x00_adjust_sdev_qdepth_down); + ql_dbg(ql_dbg_io, fcport->vha, 0x301b, + "QUEUE FULL detected.\n"); break; } + logit = 0; if (lscsi_status != SS_CHECK_CONDITION) break; @@ -1210,56 +1818,69 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) if (!(scsi_status & SS_SENSE_LEN_VALID)) break; - qla2x00_handle_sense(sp, sense_data, sense_len); + qla2x00_handle_sense(sp, sense_data, par_sense_len, sense_len, + rsp, res); break; case CS_DATA_UNDERRUN: - resid = resid_len; /* Use F/W calculated residual length. */ - if (IS_FWI2_CAPABLE(ha)) { - if (scsi_status & SS_RESIDUAL_UNDER && - resid != fw_resid_len) { - scsi_status &= ~SS_RESIDUAL_UNDER; - lscsi_status = 0; - } - resid = fw_resid_len; - } - + resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len; + scsi_set_resid(cp, resid); if (scsi_status & SS_RESIDUAL_UNDER) { - scsi_set_resid(cp, resid); - CMD_RESID_LEN(cp) = resid; - } else { - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%lld) UNDERRUN status detected " - "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x " - "os_underflow=0x%x\n", ha->host_no, - cp->device->id, (unsigned long long)cp->device->lun, comp_status, - scsi_status, resid_len, resid, cp->cmnd[0], - cp->underflow)); + if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) { + ql_dbg(ql_dbg_io, fcport->vha, 0x301d, + "Dropped frame(s) detected " + "(0x%x of 0x%x bytes).\n", + resid, scsi_bufflen(cp)); + res = DID_ERROR << 16 | lscsi_status; + goto check_scsi_status; + } + + if (!lscsi_status && + ((unsigned)(scsi_bufflen(cp) - resid) < + cp->underflow)) { + ql_dbg(ql_dbg_io, fcport->vha, 0x301e, + "Mid-layer underflow " + "detected (0x%x of 0x%x bytes).\n", + resid, scsi_bufflen(cp)); + + res = DID_ERROR << 16; + break; + } + } else if (lscsi_status != SAM_STAT_TASK_SET_FULL && + lscsi_status != SAM_STAT_BUSY) { + /* + * scsi status of task set and busy are considered to be + * task not completed. + */ + + ql_dbg(ql_dbg_io, fcport->vha, 0x301f, + "Dropped frame(s) detected (0x%x " + "of 0x%x bytes).\n", resid, + scsi_bufflen(cp)); + + res = DID_ERROR << 16 | lscsi_status; + goto check_scsi_status; + } else { + ql_dbg(ql_dbg_io, fcport->vha, 0x3030, + "scsi_status: 0x%x, lscsi_status: 0x%x\n", + scsi_status, lscsi_status); } + res = DID_OK << 16 | lscsi_status; + logit = 0; + +check_scsi_status: /* * Check to see if SCSI Status is non zero. If so report SCSI * Status. */ if (lscsi_status != 0) { - cp->result = DID_OK << 16 | lscsi_status; - if (lscsi_status == SAM_STAT_TASK_SET_FULL) { - DEBUG2(printk(KERN_INFO - "scsi(%ld): QUEUE FULL status detected " - "0x%x-0x%x.\n", ha->host_no, comp_status, - scsi_status)); - - /* - * Adjust queue depth for all luns on the - * port. - */ - fcport->last_queue_full = jiffies; - starget_for_each_device( - cp->device->sdev_target, fcport, - qla2x00_adjust_sdev_qdepth_down); + ql_dbg(ql_dbg_io, fcport->vha, 0x3020, + "QUEUE FULL detected.\n"); + logit = 1; break; } if (lscsi_status != SS_CHECK_CONDITION) @@ -1269,141 +1890,71 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) if (!(scsi_status & SS_SENSE_LEN_VALID)) break; - qla2x00_handle_sense(sp, sense_data, sense_len); - } else { - /* - * If RISC reports underrun and target does not report - * it then we must have a lost frame, so tell upper - * layer to retry it by reporting a bus busy. - */ - if (!(scsi_status & SS_RESIDUAL_UNDER)) { - DEBUG2(printk("scsi(%ld:%d:%d:%lld) Dropped " - "frame(s) detected (%x of %x bytes)..." - "retrying command.\n", ha->host_no, - cp->device->channel, cp->device->id, - (unsigned long long)cp->device->lun, resid, - scsi_bufflen(cp))); - - cp->result = DID_BUS_BUSY << 16; - break; - } - - /* Handle mid-layer underflow */ - if ((unsigned)(scsi_bufflen(cp) - resid) < - cp->underflow) { - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%lld): Mid-layer underflow " - "detected (%x of %x bytes)...returning " - "error status.\n", ha->host_no, - cp->device->channel, cp->device->id, - (unsigned long long)cp->device->lun, resid, - scsi_bufflen(cp)); - - cp->result = DID_ERROR << 16; - break; - } - - /* Everybody online, looking good... */ - cp->result = DID_OK << 16; + qla2x00_handle_sense(sp, sense_data, par_sense_len, + sense_len, rsp, res); } break; - case CS_DATA_OVERRUN: - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%lld): OVERRUN status detected 0x%x-0x%x\n", - ha->host_no, cp->device->id, (unsigned long long)cp->device->lun, - comp_status, scsi_status)); - DEBUG2(printk(KERN_INFO - "CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", - cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3], - cp->cmnd[4], cp->cmnd[5])); - DEBUG2(printk(KERN_INFO - "PID=0x%lx req=0x%x xtra=0x%x -- returning DID_ERROR " - "status!\n", - cp->serial_number, scsi_bufflen(cp), resid_len)); - - cp->result = DID_ERROR << 16; - break; - case CS_PORT_LOGGED_OUT: case CS_PORT_CONFIG_CHG: case CS_PORT_BUSY: case CS_INCOMPLETE: case CS_PORT_UNAVAILABLE: - /* - * If the port is in Target Down state, return all IOs for this - * Target with DID_NO_CONNECT ELSE Queue the IOs in the - * retry_queue. - */ - DEBUG2(printk("scsi(%ld:%d:%lld): status_entry: Port Down " - "pid=%ld, compl status=0x%x, port state=0x%x\n", - ha->host_no, cp->device->id, (unsigned long long)cp->device->lun, - cp->serial_number, comp_status, - atomic_read(&fcport->state))); - - cp->result = DID_BUS_BUSY << 16; - if (atomic_read(&fcport->state) == FCS_ONLINE) { - qla2x00_mark_device_lost(ha, fcport, 1, 1); - } - break; - + case CS_TIMEOUT: case CS_RESET: - DEBUG2(printk(KERN_INFO - "scsi(%ld): RESET status detected 0x%x-0x%x.\n", - ha->host_no, comp_status, scsi_status)); - cp->result = DID_RESET << 16; + /* + * We are going to have the fc class block the rport + * while we try to recover so instruct the mid layer + * to requeue until the class decides how to handle this. + */ + res = DID_TRANSPORT_DISRUPTED << 16; + + if (comp_status == CS_TIMEOUT) { + if (IS_FWI2_CAPABLE(ha)) + break; + else if ((le16_to_cpu(sts->status_flags) & + SF_LOGOUT_SENT) == 0) + break; + } + + ql_dbg(ql_dbg_io, fcport->vha, 0x3021, + "Port down status: port-state=0x%x.\n", + atomic_read(&fcport->state)); + + if (atomic_read(&fcport->state) == FCS_ONLINE) + qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); break; case CS_ABORTED: - /* - * hv2.19.12 - DID_ABORT does not retry the request if we - * aborted this request then abort otherwise it must be a - * reset. - */ - DEBUG2(printk(KERN_INFO - "scsi(%ld): ABORT status detected 0x%x-0x%x.\n", - ha->host_no, comp_status, scsi_status)); - - cp->result = DID_RESET << 16; + res = DID_RESET << 16; break; - case CS_TIMEOUT: - cp->result = DID_BUS_BUSY << 16; - - if (IS_FWI2_CAPABLE(ha)) { - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%d:%lld): TIMEOUT status detected " - "0x%x-0x%x\n", ha->host_no, cp->device->channel, - cp->device->id, (unsigned long long)cp->device->lun, - comp_status, scsi_status)); - break; - } - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%d:%lld): TIMEOUT status detected 0x%x-0x%x " - "sflags=%x.\n", ha->host_no, cp->device->channel, - cp->device->id, (unsigned long long)cp->device->lun, comp_status, - scsi_status, le16_to_cpu(sts->status_flags))); - - /* Check to see if logout occurred. */ - if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT)) - qla2x00_mark_device_lost(ha, fcport, 1, 1); + case CS_DIF_ERROR: + logit = qla2x00_handle_dif_error(sp, sts24); break; - default: - DEBUG3(printk("scsi(%ld): Error detected (unknown status) " - "0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); - qla_printk(KERN_INFO, ha, - "Unknown status detected 0x%x-0x%x.\n", - comp_status, scsi_status); - - cp->result = DID_ERROR << 16; + res = DID_ERROR << 16; break; } - /* Place command on done queue. */ - if (ha->status_srb == NULL) - qla2x00_sp_compl(ha, sp); +out: + if (logit) + ql_dbg(ql_dbg_io, fcport->vha, 0x3022, + "FCP command status: 0x%x-0x%x (0x%x) " + "nexus=%ld:%d:%lld portid=%02x%02x%02x oxid=0x%x " + "cdb=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x len=0x%x " + "rsp_info=0x%x resid=0x%x fw_resid=0x%x.\n", + comp_status, scsi_status, res, vha->host_no, + cp->device->id, (u64)cp->device->lun, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, ox_id, + cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3], + cp->cmnd[4], cp->cmnd[5], cp->cmnd[6], cp->cmnd[7], + cp->cmnd[8], cp->cmnd[9], scsi_bufflen(cp), rsp_info_len, + resid_len, fw_resid_len); + + if (rsp->status_srb == NULL) + sp->done(ha, sp, res); } /** @@ -1414,45 +1965,53 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) * Extended sense data. */ static void -qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt) +qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) { - uint8_t sense_sz = 0; - srb_t *sp = ha->status_srb; + uint8_t sense_sz = 0; + struct qla_hw_data *ha = rsp->hw; + struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev); + srb_t *sp = rsp->status_srb; struct scsi_cmnd *cp; + uint32_t sense_len; + uint8_t *sense_ptr; - if (sp != NULL && sp->request_sense_length != 0) { - cp = sp->cmd; - if (cp == NULL) { - DEBUG2(printk("%s(): Cmd already returned back to OS " - "sp=%p.\n", __func__, sp)); - qla_printk(KERN_INFO, ha, - "cmd is NULL: already returned to OS (sp=%p)\n", - sp); + if (!sp || !GET_CMD_SENSE_LEN(sp)) + return; - ha->status_srb = NULL; - return; - } + sense_len = GET_CMD_SENSE_LEN(sp); + sense_ptr = GET_CMD_SENSE_PTR(sp); - if (sp->request_sense_length > sizeof(pkt->data)) { - sense_sz = sizeof(pkt->data); - } else { - sense_sz = sp->request_sense_length; - } + cp = GET_CMD_SP(sp); + if (cp == NULL) { + ql_log(ql_log_warn, vha, 0x3025, + "cmd is NULL: already returned to OS (sp=%p).\n", sp); - /* Move sense data. */ - if (IS_FWI2_CAPABLE(ha)) - host_to_fcp_swap(pkt->data, sizeof(pkt->data)); - memcpy(sp->request_sense_ptr, pkt->data, sense_sz); - DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz)); + rsp->status_srb = NULL; + return; + } - sp->request_sense_ptr += sense_sz; - sp->request_sense_length -= sense_sz; + if (sense_len > sizeof(pkt->data)) + sense_sz = sizeof(pkt->data); + else + sense_sz = sense_len; - /* Place command on done queue. */ - if (sp->request_sense_length == 0) { - ha->status_srb = NULL; - qla2x00_sp_compl(ha, sp); - } + /* Move sense data. */ + if (IS_FWI2_CAPABLE(ha)) + host_to_fcp_swap(pkt->data, sizeof(pkt->data)); + memcpy(sense_ptr, pkt->data, sense_sz); + ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c, + sense_ptr, sense_sz); + + sense_len -= sense_sz; + sense_ptr += sense_sz; + + SET_CMD_SENSE_PTR(sp, sense_ptr); + SET_CMD_SENSE_LEN(sp, sense_len); + + /* Place command on done queue. */ + if (sense_len == 0) { + rsp->status_srb = NULL; + sp->done(ha, sp, cp->result); } } @@ -1462,98 +2021,39 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt) * @pkt: Entry pointer */ static void -qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt) +qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) { srb_t *sp; + struct qla_hw_data *ha = vha->hw; + const char func[] = "ERROR-IOCB"; + uint16_t que = MSW(pkt->handle); + struct req_que *req = NULL; + int res = DID_ERROR << 16; -#if defined(QL_DEBUG_LEVEL_2) - qla_printk(KERN_ERR, ha, "%s: Error entry with type %x:\n", __func__, - pkt->entry_type); - if (pkt->entry_status & RF_INV_E_ORDER) { - qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order:\n", __func__); - qla2x00_dump_buffer((void *)pkt, sizeof(*pkt)); - } else if (pkt->entry_status & RF_INV_E_COUNT) - qla_printk(KERN_ERR, ha, "%s: Invalid Entry Count\n", __func__); - else if (pkt->entry_status & RF_INV_E_PARAM) - qla_printk(KERN_ERR, ha, - "%s: Invalid Entry Parameter\n", __func__); - else if (pkt->entry_status & RF_INV_E_TYPE) - qla_printk(KERN_ERR, ha, "%s: Invalid Entry Type\n", __func__); - else if (pkt->entry_status & RF_BUSY) - qla_printk(KERN_ERR, ha, "%s: Busy\n", __func__); - else - qla_printk(KERN_ERR, ha, "%s: UNKNOWN flag error\n", __func__); -#endif + ql_dbg(ql_dbg_async, vha, 0x502a, + "type of error status in response: 0x%x\n", pkt->entry_status); - /* Validate handle. */ - if (pkt->handle < MAX_OUTSTANDING_COMMANDS) - sp = ha->outstanding_cmds[pkt->handle]; - else - sp = NULL; + if (que >= ha->max_req_queues || !ha->req_q_map[que]) + goto fatal; + req = ha->req_q_map[que]; + + if (pkt->entry_status & RF_BUSY) + res = DID_BUS_BUSY << 16; + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (sp) { - /* Free outstanding command slot. */ - ha->outstanding_cmds[pkt->handle] = NULL; - - /* Bad payload or header */ - if (pkt->entry_status & - (RF_INV_E_ORDER | RF_INV_E_COUNT | - RF_INV_E_PARAM | RF_INV_E_TYPE)) { - sp->cmd->result = DID_ERROR << 16; - } else if (pkt->entry_status & RF_BUSY) { - sp->cmd->result = DID_BUS_BUSY << 16; - } else { - sp->cmd->result = DID_ERROR << 16; - } - qla2x00_sp_compl(ha, sp); - - } else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type == - COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7) { - DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, - "Error entry - invalid handle\n"); - - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - } -} - -/** - * qla2x00_ms_entry() - Process a Management Server entry. - * @ha: SCSI driver HA context - * @index: Response queue out pointer - */ -static void -qla2x00_ms_entry(scsi_qla_host_t *ha, ms_iocb_entry_t *pkt) -{ - srb_t *sp; - - DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n", - __func__, ha->host_no, pkt, pkt->handle1)); - - /* Validate handle. */ - if (pkt->handle1 < MAX_OUTSTANDING_COMMANDS) - sp = ha->outstanding_cmds[pkt->handle1]; - else - sp = NULL; - - if (sp == NULL) { - DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, "MS entry - invalid handle\n"); - - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + sp->done(ha, sp, res); return; } +fatal: + ql_log(ql_log_warn, vha, 0x5030, + "Error entry - invalid handle/queue.\n"); - CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->status); - CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status; - - /* Free outstanding command slot. */ - ha->outstanding_cmds[pkt->handle1] = NULL; - - qla2x00_sp_compl(ha, sp); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); } /** @@ -1562,70 +2062,61 @@ qla2x00_ms_entry(scsi_qla_host_t *ha, ms_iocb_entry_t *pkt) * @mb0: Mailbox0 register */ static void -qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0) +qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) { uint16_t cnt; + uint32_t mboxes; uint16_t __iomem *wptr; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + /* Read all mbox registers? */ + mboxes = (1 << ha->mbx_count) - 1; + if (!ha->mcp) + ql_dbg(ql_dbg_async, vha, 0x504e, "MBX pointer ERRROR.\n"); + else + mboxes = ha->mcp->in_mb; + /* Load return mailbox registers. */ ha->flags.mbox_int = 1; ha->mailbox_out[0] = mb0; + mboxes >>= 1; wptr = (uint16_t __iomem *)®->mailbox1; for (cnt = 1; cnt < ha->mbx_count; cnt++) { - ha->mailbox_out[cnt] = RD_REG_WORD(wptr); + if (mboxes & BIT_0) + ha->mailbox_out[cnt] = RD_REG_WORD(wptr); + + mboxes >>= 1; wptr++; } - - if (ha->mcp) { - DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n", - __func__, ha->host_no, ha->mcp->mb[0])); - } else { - DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n", - __func__, ha->host_no)); - } - -#if defined(QL_DEBUG_LEVEL_1) - printk(KERN_INFO "scsi(%ld): Mailbox registers:", ha->host_no); - for (cnt = 0; cnt < ha->mbx_count; cnt++) { - if ((cnt % 4) == 0) - printk(KERN_CONT "\n"); - printk("mbox %02d: 0x%04x ", cnt, ha->mailbox_out[cnt]); - } - printk(KERN_CONT "\n"); -#endif } #ifdef CONFIG_SCSI_QLA2XXX_TARGET /** * qla24xx_process_atio_queue() - Process ATIO queue entries. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context */ -static void -qla24xx_process_atio_queue(struct scsi_qla_host *ha) +void +qla24xx_process_atio_queue(struct scsi_qla_host *vha) { - struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; atio_t *pkt; int cnt, i; + struct qla_hw_data *ha = vha->hw; - if (!ha->flags.online) + if (!vha->flags.online) { + ql_dbg(ql_dbg_tgt, vha, 0xffff, "%s(%ld): Delaying ATIO IOCBs\n", + __func__, vha->host_no); + ha->atio_ignored = 1; return; + } while (ha->atio_ring_ptr->signature != ATIO_PROCESSED) { pkt = ha->atio_ring_ptr; cnt = pkt->entry_count; - DEBUG5(printk(KERN_INFO "%s(): IOCB data:\n", __func__)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)pkt, RESPONSE_ENTRY_SIZE)); - - DEBUG5(printk(KERN_WARNING - "%s: calling tgt24_atio_pkt %p (type %02X, " - "count %d)\n", __func__, qla_target.tgt24_atio_pkt, - pkt->entry_type, cnt);); - if (likely(qla_target.tgt24_atio_pkt)) - qla_target.tgt24_atio_pkt(ha, (atio7_entry_t *)pkt); + qla_target.tgt24_atio_pkt(vha, (atio7_entry_t *)pkt); for (i = 0; i < cnt; i++) { ha->atio_ring_index++; @@ -1642,7 +2133,7 @@ qla24xx_process_atio_queue(struct scsi_qla_host *ha) } /* Adjust ring index */ - WRT_REG_DWORD(®->atio_q_out, ha->atio_ring_index); + WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), ha->atio_ring_index); } #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ @@ -1650,34 +2141,28 @@ qla24xx_process_atio_queue(struct scsi_qla_host *ha) * qla24xx_process_response_queue() - Process response queue entries. * @ha: SCSI driver HA context */ -void -qla24xx_process_response_queue(struct scsi_qla_host *ha) +void qla24xx_process_response_queue(struct scsi_qla_host *vha, + struct rsp_que *rsp) { - struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; struct sts_entry_24xx *pkt; + struct qla_hw_data *ha = vha->hw; - if (!ha->flags.online) + if (!vha->flags.online) return; - while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) { - pkt = (struct sts_entry_24xx *)ha->response_ring_ptr; + while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) { + pkt = (struct sts_entry_24xx *)rsp->ring_ptr; - DEBUG5(printk(KERN_INFO "%s(): IOCB data:\n", __func__)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)pkt, RESPONSE_ENTRY_SIZE)); - - ha->rsp_ring_index++; - if (ha->rsp_ring_index == ha->response_q_length) { - ha->rsp_ring_index = 0; - ha->response_ring_ptr = ha->response_ring; + rsp->ring_index++; + if (rsp->ring_index == rsp->length) { + rsp->ring_index = 0; + rsp->ring_ptr = rsp->ring; } else { - ha->response_ring_ptr++; + rsp->ring_ptr++; } if (pkt->entry_status != 0) { - DEBUG3(printk(KERN_INFO - "scsi(%ld): Process error entry.\n", ha->host_no)); - - qla2x00_error_entry(ha, (sts_entry_t *) pkt); + qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt); #ifdef CONFIG_SCSI_QLA2XXX_TARGET switch (pkt->entry_type) { case ABTS_RECV_24XX: @@ -1686,77 +2171,67 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha) case NOTIFY_ACK_TYPE: break; default: - ((response_t *)pkt)->signature = RESPONSE_PROCESSED; + ((response_t *)pkt)->signature = + RESPONSE_PROCESSED; wmb(); continue; } -#endif +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ + ((response_t *)pkt)->signature = RESPONSE_PROCESSED; + wmb(); + continue; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ } switch (pkt->entry_type) { case STATUS_TYPE: - qla2x00_status_entry(ha, pkt); + qla2x00_status_entry(vha, rsp, pkt); break; case STATUS_CONT_TYPE: - qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt); + qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt); break; case VP_RPT_ID_IOCB_TYPE: - qla24xx_report_id_acquisition(ha, + qla24xx_report_id_acquisition(vha, (struct vp_rpt_id_entry_24xx *)pkt); break; #ifdef CONFIG_SCSI_QLA2XXX_TARGET case ABTS_RECV_24XX: /* ensure that the ATIO queue is empty */ - qla24xx_process_atio_queue(ha); + qla24xx_process_atio_queue(vha); /* go through */ case ABTS_RESP_24XX: case CTIO_TYPE7: case NOTIFY_ACK_TYPE: - DEBUG5(printk(KERN_WARNING - "qla2x00_response_pkt: " - "calling tgt_response_pkt %p " - "(type %02X)\n", - qla_target.tgt_response_pkt, - pkt->entry_type);); if (qla_target.tgt_response_pkt) - qla_target.tgt_response_pkt(ha, - (response_t *)pkt); + qla_target.tgt_response_pkt(vha, + (response_t *)pkt); break; #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + case LOGINOUT_PORT_IOCB_TYPE: + qla24xx_logio_entry(vha, rsp->req, + (struct logio_entry_24xx *)pkt); + break; + case TSK_MGMT_IOCB_TYPE: + qla24xx_tm_iocb_entry(vha, rsp->req, + (struct tsk_mgmt_entry *)pkt); + break; + case CT_IOCB_TYPE: + qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE); + break; + case ELS_IOCB_TYPE: + qla24xx_els_ct_entry(vha, rsp->req, pkt, ELS_IOCB_TYPE); + break; case MARKER_TYPE: - break; - case MS_IOCB_TYPE: - if (ha->outstanding_cmds[pkt->handle]) - qla24xx_ms_entry(ha, (void *)pkt); - else { - if (ha->pass_thru_cmd_result) - DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru cmd result on.\n")); - if (!ha->pass_thru_cmd_in_process) - DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru in process off.\n")); - - ha->pass_thru_cmd_result = 1; - complete(&ha->pass_thru_intr_comp); - } - break; - case ELS_IOCB_TYPE: - if (ha->pass_thru_cmd_result) - DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru cmd result on.\n")); - if (!ha->pass_thru_cmd_in_process) - DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru in process off.\n")); - - ha->pass_thru_cmd_result = 1; - complete(&ha->pass_thru_intr_comp); + /* Do nothing in this case, this check is to prevent it + * from falling into default case + */ break; default: /* Type Not Supported. */ - DEBUG4(printk(KERN_WARNING - "scsi(%ld): Received unknown response pkt type %x " + ql_dbg(ql_dbg_async, vha, 0x5042, + "Received unknown response pkt type %x " "entry status=%x.\n", - ha->host_no, pkt->entry_type, pkt->entry_status)); + pkt->entry_type, pkt->entry_status); break; } ((response_t *)pkt)->signature = RESPONSE_PROCESSED; @@ -1764,17 +2239,22 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha) } /* Adjust ring index */ - WRT_REG_DWORD(®->rsp_q_out, ha->rsp_ring_index); + if (IS_QLA82XX(ha)) { + struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; + WRT_REG_DWORD(®->rsp_q_out[0], rsp->ring_index); + } else + WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index); } static void -qla2xxx_check_risc_status(scsi_qla_host_t *ha) +qla2xxx_check_risc_status(scsi_qla_host_t *vha) { int rval; uint32_t cnt; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - if (!IS_QLA25XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha)) return; rval = QLA_SUCCESS; @@ -1793,6 +2273,7 @@ qla2xxx_check_risc_status(scsi_qla_host_t *ha) goto next_test; WRT_REG_DWORD(®->iobase_window, 0x0003); + rval = QLA_SUCCESS; for (cnt = 100; (RD_REG_DWORD(®->iobase_window) & BIT_0) == 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) { @@ -1806,7 +2287,8 @@ qla2xxx_check_risc_status(scsi_qla_host_t *ha) next_test: if (RD_REG_DWORD(®->iobase_c8) & BIT_3) - qla_printk(KERN_INFO, ha, "Additional code -- 0x55AA.\n"); + ql_log(ql_log_info, vha, 0x504c, + "Additional code -- 0x55AA.\n"); done: WRT_REG_DWORD(®->iobase_window, 0x0000); @@ -1814,7 +2296,7 @@ done: } /** - * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. + * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP24xx. * @irq: * @dev_id: SCSI driver HA context * @@ -1825,47 +2307,49 @@ done: irqreturn_t qla24xx_intr_handler(int irq, void *dev_id) { - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; struct device_reg_24xx __iomem *reg; int status; unsigned long iter; uint32_t stat; uint32_t hccr; uint16_t mb[4]; + struct rsp_que *rsp; unsigned long flags; - ha = (scsi_qla_host_t *) dev_id; - if (!ha) { - printk(KERN_INFO - "%s(): NULL host pointer\n", __func__); + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0x5059, + "%s: NULL response queue pointer.\n", __func__); return IRQ_NONE; } + ha = rsp->hw; reg = &ha->iobase->isp24; status = 0; + if (unlikely(pci_channel_offline(ha->pdev))) + return IRQ_HANDLED; + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); for (iter = 50; iter--; ) { stat = RD_REG_DWORD(®->host_status); if (stat & HSRX_RISC_PAUSED) { - if (pci_channel_offline(ha->pdev)) + if (unlikely(pci_channel_offline(ha->pdev))) break; - if (ha->hw_event_pause_errors == 0) - qla2x00_post_hwe_work(ha, HW_EVENT_PARITY_ERR, - 0, MSW(stat), LSW(stat)); - else if (ha->hw_event_pause_errors < 0xffffffff) - ha->hw_event_pause_errors++; - hccr = RD_REG_DWORD(®->hccr); - qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " - "Dumping firmware!\n", hccr); + ql_log(ql_log_warn, vha, 0x504b, + "RISC paused -- HCCR=%x, Dumping firmware.\n", + hccr); - qla2xxx_check_risc_status(ha); + qla2xxx_check_risc_status(vha); - ha->isp_ops->fw_dump(ha, 1); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ha->isp_ops->fw_dump(vha, 1); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; } else if ((stat & HSRX_RISC_INT) == 0) break; @@ -1875,7 +2359,7 @@ qla24xx_intr_handler(int irq, void *dev_id) case 0x2: case 0x10: case 0x11: - qla24xx_mbx_completion(ha, MSW(stat)); + qla24xx_mbx_completion(vha, MSW(stat)); status |= MBX_INTERRUPT; break; @@ -1884,24 +2368,24 @@ qla24xx_intr_handler(int irq, void *dev_id) mb[1] = RD_REG_WORD(®->mailbox1); mb[2] = RD_REG_WORD(®->mailbox2); mb[3] = RD_REG_WORD(®->mailbox3); - qla2x00_async_event(ha, mb); + qla2x00_async_event(vha, rsp, mb); break; case 0x13: - qla24xx_process_response_queue(ha); + case 0x14: + qla24xx_process_response_queue(vha, rsp); break; #ifdef CONFIG_SCSI_QLA2XXX_TARGET case 0x1C: /* ATIO queue updated */ - qla24xx_process_atio_queue(ha); + qla24xx_process_atio_queue(vha); break; case 0x1D: /* ATIO and response queues updated */ - qla24xx_process_atio_queue(ha); - qla24xx_process_response_queue(ha); + qla24xx_process_atio_queue(vha); + qla24xx_process_response_queue(vha, rsp); break; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ default: - DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " - "(%d).\n", - ha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x504f, + "Unrecognized interrupt type (%d).\n", stat * 0xff); break; } WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); @@ -1914,110 +2398,134 @@ qla24xx_intr_handler(int irq, void *dev_id) set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); complete(&ha->mbx_intr_comp); } - return IRQ_HANDLED; } -/** - * qla24xx_ms_entry() - Process a Management Server entry. - * @ha: SCSI driver HA context - * @index: Response queue out pointer - */ -static void -qla24xx_ms_entry(scsi_qla_host_t *ha, struct ct_entry_24xx *pkt) -{ - srb_t *sp; - - DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n", - __func__, ha->host_no, pkt, pkt->handle)); - - DEBUG9(printk("%s: ct pkt dump:\n", __func__)); - DEBUG9(qla2x00_dump_buffer((void *)pkt, sizeof(struct ct_entry_24xx))); - - /* Validate handle. */ - if (pkt->handle < MAX_OUTSTANDING_COMMANDS) - sp = ha->outstanding_cmds[pkt->handle]; - else - sp = NULL; - - if (sp == NULL) { - DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n", - ha->host_no)); - DEBUG10(printk("scsi(%ld): MS entry - invalid handle\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, "MS entry - invalid handle %d\n", - pkt->handle); - - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - return; - } - - CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->comp_status); - CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status; - - /* Free outstanding command slot. */ - ha->outstanding_cmds[pkt->handle] = NULL; - - qla2x00_sp_compl(ha, sp); -} - static irqreturn_t qla24xx_msix_rsp_q(int irq, void *dev_id) { - scsi_qla_host_t *ha; + struct qla_hw_data *ha; + struct rsp_que *rsp; struct device_reg_24xx __iomem *reg; + struct scsi_qla_host *vha; unsigned long flags; - ha = dev_id; + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0x505a, + "%s: NULL response queue pointer.\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; reg = &ha->iobase->isp24; spin_lock_irqsave(&ha->hardware_lock, flags); - qla24xx_process_response_queue(ha); - WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); - + vha = pci_get_drvdata(ha->pdev); + qla24xx_process_response_queue(vha, rsp); + if (!ha->disable_msix_handshake) { + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); + RD_REG_DWORD_RELAXED(®->hccr); + } spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } +static irqreturn_t +qla25xx_msix_rsp_q(int irq, void *dev_id) +{ + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_24xx __iomem *reg; + unsigned long flags; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0x505b, + "%s: NULL response queue pointer.\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; + + /* Clear the interrupt, if enabled, for this response queue */ + if (!ha->disable_msix_handshake) { + reg = &ha->iobase->isp24; + spin_lock_irqsave(&ha->hardware_lock, flags); + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); + RD_REG_DWORD_RELAXED(®->hccr); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } + queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +static irqreturn_t +qla83xx_msix_atio_q(int irq, void *dev_id) +{ + struct rsp_que *rsp; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + unsigned long flags; + + rsp = (struct rsp_que *) dev_id; + ha = rsp->hw; + vha = pci_get_drvdata(ha->pdev); + + spin_lock_irqsave(&ha->hardware_lock, flags); + + qla24xx_process_atio_queue(vha); + qla24xx_process_response_queue(vha, rsp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + static irqreturn_t qla24xx_msix_default(int irq, void *dev_id) { - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; struct device_reg_24xx __iomem *reg; int status; uint32_t stat; uint32_t hccr; uint16_t mb[4]; - unsigned long flags; + unsigned long flags; - ha = dev_id; + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0x505c, + "%s: NULL response queue pointer.\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; reg = &ha->iobase->isp24; status = 0; spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); do { stat = RD_REG_DWORD(®->host_status); if (stat & HSRX_RISC_PAUSED) { - if (pci_channel_offline(ha->pdev)) + if (unlikely(pci_channel_offline(ha->pdev))) break; - if (ha->hw_event_pause_errors == 0) - qla2x00_post_hwe_work(ha, HW_EVENT_PARITY_ERR, - 0, MSW(stat), LSW(stat)); - else if (ha->hw_event_pause_errors < 0xffffffff) - ha->hw_event_pause_errors++; - hccr = RD_REG_DWORD(®->hccr); - qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " - "Dumping firmware!\n", hccr); + ql_log(ql_log_info, vha, 0x5050, + "RISC paused -- HCCR=%x, Dumping firmware.\n", + hccr); - qla2xxx_check_risc_status(ha); + qla2xxx_check_risc_status(vha); - ha->isp_ops->fw_dump(ha, 1); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ha->isp_ops->fw_dump(vha, 1); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; } else if ((stat & HSRX_RISC_INT) == 0) break; @@ -2027,7 +2535,7 @@ qla24xx_msix_default(int irq, void *dev_id) case 0x2: case 0x10: case 0x11: - qla24xx_mbx_completion(ha, MSW(stat)); + qla24xx_mbx_completion(vha, MSW(stat)); status |= MBX_INTERRUPT; break; @@ -2036,24 +2544,24 @@ qla24xx_msix_default(int irq, void *dev_id) mb[1] = RD_REG_WORD(®->mailbox1); mb[2] = RD_REG_WORD(®->mailbox2); mb[3] = RD_REG_WORD(®->mailbox3); - qla2x00_async_event(ha, mb); + qla2x00_async_event(vha, rsp, mb); break; case 0x13: - qla24xx_process_response_queue(ha); + case 0x14: + qla24xx_process_response_queue(vha, rsp); break; #ifdef CONFIG_SCSI_QLA2XXX_TARGET case 0x1C: /* ATIO queue updated */ - qla24xx_process_atio_queue(ha); + qla24xx_process_atio_queue(vha); break; case 0x1D: /* ATIO and response queues updated */ - qla24xx_process_atio_queue(ha); - qla24xx_process_response_queue(ha); + qla24xx_process_atio_queue(vha); + qla24xx_process_response_queue(vha, rsp); break; #endif default: - DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " - "(%d).\n", - ha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x5051, + "Unrecognized interrupt type (%d).\n", stat & 0xff); break; } WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); @@ -2072,141 +2580,235 @@ qla24xx_msix_default(int irq, void *dev_id) /* Interrupt handling helpers. */ struct qla_init_msix_entry { - uint16_t entry; - uint16_t index; const char *name; irq_handler_t handler; }; -static struct qla_init_msix_entry imsix_entries[QLA_MSIX_ENTRIES] = { - { QLA_MSIX_DEFAULT, QLA_MIDX_DEFAULT, - "qla2xxx (default)", qla24xx_msix_default }, - - { QLA_MSIX_RSP_Q, QLA_MIDX_RSP_Q, - "qla2xxx (rsp_q)", qla24xx_msix_rsp_q }, +static struct qla_init_msix_entry msix_entries[3] = { + { "qla2xxx (default)", qla24xx_msix_default }, + { "qla2xxx (rsp_q)", qla24xx_msix_rsp_q }, + { "qla2xxx (multiq)", qla25xx_msix_rsp_q }, }; +static struct qla_init_msix_entry qla82xx_msix_entries[2] = { + { "qla2xxx (default)", qla82xx_msix_default }, + { "qla2xxx (rsp_q)", qla82xx_msix_rsp_q }, +}; + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +static struct qla_init_msix_entry qla83xx_msix_entries[3] = { + { "qla2xxx (default)", qla24xx_msix_default }, + { "qla2xxx (rsp_q)", qla24xx_msix_rsp_q }, + { "qla2xxx (atio_q)", qla83xx_msix_atio_q }, +}; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + static void -qla24xx_disable_msix(scsi_qla_host_t *ha) +qla24xx_disable_msix(struct qla_hw_data *ha) { int i; struct qla_msix_entry *qentry; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); - for (i = 0; i < QLA_MSIX_ENTRIES; i++) { - qentry = &ha->msix_entries[imsix_entries[i].index]; + for (i = 0; i < ha->msix_count; i++) { + qentry = &ha->msix_entries[i]; if (qentry->have_irq) - free_irq(qentry->msix_vector, ha); + free_irq(qentry->vector, qentry->rsp); } pci_disable_msix(ha->pdev); + kfree(ha->msix_entries); + ha->msix_entries = NULL; + ha->flags.msix_enabled = 0; + ql_dbg(ql_dbg_init, vha, 0x0042, + "Disabled the MSI.\n"); } static int -qla24xx_enable_msix(scsi_qla_host_t *ha) +qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) { +#define MIN_MSIX_COUNT 2 int i, ret; - struct msix_entry entries[QLA_MSIX_ENTRIES]; + struct msix_entry *entries; struct qla_msix_entry *qentry; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); - for (i = 0; i < QLA_MSIX_ENTRIES; i++) - entries[i].entry = imsix_entries[i].entry; + entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count, + GFP_KERNEL); + if (!entries) { + ql_log(ql_log_warn, vha, 0x00bc, + "Failed to allocate memory for msix_entry.\n"); + return -ENOMEM; + } - ret = pci_enable_msix(ha->pdev, entries, ARRAY_SIZE(entries)); + for (i = 0; i < ha->msix_count; i++) + entries[i].entry = i; + + ret = pci_enable_msix(ha->pdev, entries, ha->msix_count); if (ret) { - qla_printk(KERN_WARNING, ha, - "MSI-X: Failed to enable support -- %d/%d\n", - QLA_MSIX_ENTRIES, ret); + if (ret < MIN_MSIX_COUNT) + goto msix_failed; + + ql_log(ql_log_warn, vha, 0x00c6, + "MSI-X: Failed to enable support " + "-- %d/%d\n Retry with %d vectors.\n", + ha->msix_count, ret, ret); + ha->msix_count = ret; + ret = pci_enable_msix(ha->pdev, entries, ha->msix_count); + if (ret) { +msix_failed: + ql_log(ql_log_fatal, vha, 0x00c7, + "MSI-X: Failed to enable support, " + "giving up -- %d/%d.\n", + ha->msix_count, ret); + goto msix_out; + } + ha->max_rsp_queues = ha->msix_count - 1; + } + ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) * + ha->msix_count, GFP_KERNEL); + if (!ha->msix_entries) { + ql_log(ql_log_fatal, vha, 0x00c8, + "Failed to allocate memory for ha->msix_entries.\n"); + ret = -ENOMEM; goto msix_out; } ha->flags.msix_enabled = 1; - for (i = 0; i < QLA_MSIX_ENTRIES; i++) { - qentry = &ha->msix_entries[imsix_entries[i].index]; - qentry->msix_vector = entries[i].vector; - qentry->msix_entry = entries[i].entry; + for (i = 0; i < ha->msix_count; i++) { + qentry = &ha->msix_entries[i]; + qentry->vector = entries[i].vector; + qentry->entry = entries[i].entry; qentry->have_irq = 0; - ret = request_irq(qentry->msix_vector, - imsix_entries[i].handler, 0, imsix_entries[i].name, ha); + qentry->rsp = NULL; + } + + /* Enable MSI-X vectors for the base queue */ + for (i = 0; i < ha->msix_count; i++) { + qentry = &ha->msix_entries[i]; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (IS_ATIO_MSIX_CAPABLE(ha)) { + ret = request_irq(qentry->vector, + qla83xx_msix_entries[i].handler, + 0, qla83xx_msix_entries[i].name, rsp); + } else +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + if (IS_QLA82XX(ha)) { + ret = request_irq(qentry->vector, + qla82xx_msix_entries[i].handler, + 0, qla82xx_msix_entries[i].name, rsp); + } else { + ret = request_irq(qentry->vector, + msix_entries[i].handler, + 0, msix_entries[i].name, rsp); + } if (ret) { - qla_printk(KERN_WARNING, ha, - "MSI-X: Unable to register handler -- %x/%d.\n", - imsix_entries[i].index, ret); + ql_log(ql_log_fatal, vha, 0x00cb, + "MSI-X: unable to register handler -- %x/%d.\n", + qentry->vector, ret); qla24xx_disable_msix(ha); + ha->mqenable = 0; goto msix_out; } qentry->have_irq = 1; + qentry->rsp = rsp; + rsp->msix = qentry; } + /* Enable MSI-X vector for response queue update for queue 0 */ + if (IS_QLA83XX(ha)) { + if (ha->msixbase && ha->mqiobase && + (ha->max_rsp_queues > 1 || ha->max_req_queues > 1)) + ha->mqenable = 1; + } else + if (ha->mqiobase + && (ha->max_rsp_queues > 1 || ha->max_req_queues > 1)) + ha->mqenable = 1; + ql_dbg(ql_dbg_multiq, vha, 0xc005, + "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n", + ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues); + ql_dbg(ql_dbg_init, vha, 0x0055, + "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n", + ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues); + msix_out: + kfree(entries); return ret; } int -qla2x00_request_irqs(scsi_qla_host_t *ha) +qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) { int ret; device_reg_t __iomem *reg = ha->iobase; - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); /* If possible, enable MSI-X. */ - if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha)) - goto skip_msix; - - if (IS_QLA2432(ha) && (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX || - !QLA_MSIX_FW_MODE_1(ha->fw_attributes))) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n", - ha->chip_revision, ha->fw_attributes)); - - goto skip_msix; - } + if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) + goto skip_msi; if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_HP && - (ha->pdev->subsystem_device == 0x7040 || + (ha->pdev->subsystem_device == 0x7040 || ha->pdev->subsystem_device == 0x7041 || ha->pdev->subsystem_device == 0x1705)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "MSI-X: Unsupported ISP2432 SSVID/SSDID (0x%X, 0x%X).\n", - ha->pdev->subsystem_vendor, - ha->pdev->subsystem_device)); - + ql_log(ql_log_warn, vha, 0x0034, + "MSI-X: Unsupported ISP 2432 SSVID/SSDID (0x%X,0x%X).\n", + ha->pdev->subsystem_vendor, + ha->pdev->subsystem_device); goto skip_msi; } - ret = qla24xx_enable_msix(ha); + if (IS_QLA2432(ha) && (ha->pdev->revision < QLA_MSIX_CHIP_REV_24XX)) { + ql_log(ql_log_warn, vha, 0x0035, + "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n", + ha->pdev->revision, ha->fw_attributes); + goto skip_msix; + } + + ret = qla24xx_enable_msix(ha, rsp); if (!ret) { - DEBUG2(qla_printk(KERN_INFO, ha, - "MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision, - ha->fw_attributes)); + ql_dbg(ql_dbg_init, vha, 0x0036, + "MSI-X: Enabled (0x%X, 0x%X).\n", + ha->chip_revision, ha->fw_attributes); goto clear_risc_ints; } - qla_printk(KERN_WARNING, ha, - "MSI-X: Falling back-to INTa mode -- %d.\n", ret); + ql_log(ql_log_info, vha, 0x0037, + "MSI-X Falling back-to MSI mode -%d.\n", ret); skip_msix: - if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha)) + if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && + !IS_QLA8001(ha)) goto skip_msi; ret = pci_enable_msi(ha->pdev); if (!ret) { - DEBUG2(qla_printk(KERN_INFO, ha, "MSI: Enabled.\n")); + ql_dbg(ql_dbg_init, vha, 0x0038, + "MSI: Enabled.\n"); ha->flags.msi_enabled = 1; - } + } else + ql_log(ql_log_warn, vha, 0x0039, + "MSI-X; Falling back-to INTa mode -- %d.\n", ret); skip_msi: ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler, - IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha); + ha->flags.msi_enabled ? 0 : IRQF_SHARED, + QLA2XXX_DRIVER_NAME, rsp); if (ret) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x003a, "Failed to reserve interrupt %d already in use.\n", ha->pdev->irq); goto fail; } - ha->flags.inta_enabled = 1; - ha->host->irq = ha->pdev->irq; + clear_risc_ints: - ha->isp_ops->disable_intrs(ha); - spin_lock_irq(&pha->hardware_lock); + /* + * FIXME: Noted that 8014s were being dropped during NK testing. + * Timing deltas during MSI-X/INTa transitions? + */ + if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA83XX(ha)) + goto fail; + spin_lock_irq(&ha->hardware_lock); if (IS_FWI2_CAPABLE(ha)) { WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_HOST_INT); WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_RISC_INT); @@ -2215,20 +2817,52 @@ clear_risc_ints: WRT_REG_WORD(®->isp.hccr, HCCR_CLR_RISC_INT); WRT_REG_WORD(®->isp.hccr, HCCR_CLR_HOST_INT); } - spin_unlock_irq(&pha->hardware_lock); + spin_unlock_irq(&ha->hardware_lock); fail: return ret; } void -qla2x00_free_irqs(scsi_qla_host_t *ha) +qla2x00_free_irqs(scsi_qla_host_t *vha) { + struct qla_hw_data *ha = vha->hw; + struct rsp_que *rsp; + + /* + * We need to check that ha->rsp_q_map is valid in case we are called + * from a probe failure context. + */ + if (!ha->rsp_q_map || !ha->rsp_q_map[0]) + return; + rsp = ha->rsp_q_map[0]; if (ha->flags.msix_enabled) qla24xx_disable_msix(ha); - else if (ha->flags.inta_enabled) { - free_irq(ha->host->irq, ha); + else if (ha->flags.msi_enabled) { + free_irq(ha->pdev->irq, rsp); pci_disable_msi(ha->pdev); - } + } else + free_irq(ha->pdev->irq, rsp); +} + + +int qla25xx_request_irq(struct rsp_que *rsp) +{ + struct qla_hw_data *ha = rsp->hw; + struct qla_init_msix_entry *intr = &msix_entries[2]; + struct qla_msix_entry *msix = rsp->msix; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + int ret; + + ret = request_irq(msix->vector, intr->handler, 0, intr->name, rsp); + if (ret) { + ql_log(ql_log_fatal, vha, 0x00e6, + "MSI-X: Unable to register handler -- %x/%d.\n", + msix->vector, ret); + return ret; + } + msix->have_irq = 1; + msix->rsp = rsp; + return ret; } diff --git a/qla2x00t/qla_mbx.c b/qla2x00t/qla_mbx.c index a7c7015ba..ffdd00289 100644 --- a/qla2x00t/qla_mbx.c +++ b/qla2x00t/qla_mbx.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -23,36 +23,64 @@ * mb[MAX_MAILBOX_REGISTER_COUNT] = returned mailbox data. * * Returns: - * 0 : QLA_SUCCESS = cmd performed success - * 1 : QLA_FUNCTION_FAILED (error encountered) - * 6 : QLA_FUNCTION_TIMEOUT (timeout condition encountered) + * QLA_SUCCESS: cmd performed success + * QLA_FUNCTION_FAILED: (error encountered) + * QLA_FUNCTION_TIMEOUT: (timeout condition encountered) * * Context: * Kernel context. */ int -qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) +qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) { int rval; unsigned long flags = 0; device_reg_t __iomem *reg; uint8_t abort_active; uint8_t io_lock_on; - uint16_t command; + uint16_t command = 0; uint16_t *iptr; uint16_t __iomem *optr; uint32_t cnt; uint32_t mboxes; unsigned long wait_time; - scsi_qla_host_t *ha = to_qla_parent(pvha); + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + + ql_dbg(ql_dbg_mbx, vha, 0x1000, "Entered %s.\n", __func__); + + if (ha->pdev->error_state > pci_channel_io_frozen) { + ql_log(ql_log_warn, vha, 0x1001, + "error_state is greater than pci_channel_io_frozen, " + "exiting.\n"); + return QLA_FUNCTION_TIMEOUT; + } + + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_log(ql_log_warn, vha, 0x1002, + "Device in failed state, exiting.\n"); + return QLA_FUNCTION_TIMEOUT; + } reg = ha->iobase; - io_lock_on = ha->flags.init_done; + io_lock_on = base_vha->flags.init_done; rval = QLA_SUCCESS; - abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); + abort_active = test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); - DEBUG11(printk("%s(%ld): entered.\n", __func__, pvha->host_no)); + if (ha->flags.pci_channel_io_perm_failure) { + ql_log(ql_log_warn, vha, 0x1003, + "Perm failure on EEH timeout MBX, exiting.\n"); + return QLA_FUNCTION_TIMEOUT; + } + + if (ha->flags.isp82xx_fw_hung) { + /* Setting Link-Down error */ + mcp->mb[0] = MBS_LINK_DOWN_ERROR; + ql_log(ql_log_warn, vha, 0x1004, + "FW hung = %d.\n", ha->flags.isp82xx_fw_hung); + return QLA_FUNCTION_TIMEOUT; + } /* * Wait for active mailbox commands to finish by waiting at most tov @@ -61,8 +89,9 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) */ if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, mcp->tov * HZ)) { /* Timeout occurred. Return error. */ - DEBUG2_3_11(printk("%s(%ld): cmd access timeout. " - "Exiting.\n", __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x1005, + "Cmd access timeout, cmd=0x%x, Exiting.\n", + mcp->mb[0]); return QLA_FUNCTION_TIMEOUT; } @@ -70,13 +99,15 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) /* Save mailbox command for debug */ ha->mcp = mcp; - DEBUG11(printk("scsi(%ld): prepare to issue mbox cmd=0x%x.\n", - ha->host_no, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1006, + "Prepare to issue mbox cmd=0x%x.\n", mcp->mb[0]); spin_lock_irqsave(&ha->hardware_lock, flags); /* Load mailbox registers. */ - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA82XX(ha)) + optr = (uint16_t __iomem *)®->isp82.mailbox_in[0]; + else if (IS_FWI2_CAPABLE(ha) && !IS_QLA82XX(ha)) optr = (uint16_t __iomem *)®->isp24.mailbox0; else optr = (uint16_t __iomem *)MAILBOX_REG(ha, ®->isp, 0); @@ -97,34 +128,49 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) iptr++; } -#if defined(QL_DEBUG_LEVEL_1) - printk("%s(%ld): Loaded MBX registers (displayed in bytes) = \n", - __func__, ha->host_no); - qla2x00_dump_buffer((uint8_t *)mcp->mb, 16); - printk("\n"); - qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x10), 16); - printk("\n"); - qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x20), 8); - printk("\n"); - printk("%s(%ld): I/O address = %p.\n", __func__, ha->host_no, optr); - qla2x00_dump_regs(ha); -#endif + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1111, + "Loaded MBX registers (displayed in bytes) =.\n"); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1112, + (uint8_t *)mcp->mb, 16); + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1113, + ".\n"); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1114, + ((uint8_t *)mcp->mb + 0x10), 16); + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1115, + ".\n"); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1116, + ((uint8_t *)mcp->mb + 0x20), 8); + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1117, + "I/O Address = %p.\n", optr); + ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x100e); /* Issue set host interrupt command to send cmd out. */ ha->flags.mbox_int = 0; clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); /* Unlock mbx registers and wait for interrupt */ - DEBUG11(printk("%s(%ld): going to unlock irq & waiting for interrupt. " - "jiffies=%lx.\n", __func__, ha->host_no, jiffies)); + ql_dbg(ql_dbg_mbx, vha, 0x100f, + "Going to unlock irq & waiting for interrupts. " + "jiffies=%lx.\n", jiffies); /* Wait for mbx cmd completion until timeout */ - if (!abort_active && io_lock_on) { - + if ((!abort_active && io_lock_on) || IS_NOPOLLING_TYPE(ha)) { set_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA82XX(ha)) { + if (RD_REG_DWORD(®->isp82.hint) & + HINT_MBX_INT_PENDING) { + spin_unlock_irqrestore(&ha->hardware_lock, + flags); + ha->flags.mbox_busy = 0; + ql_dbg(ql_dbg_mbx, vha, 0x1010, + "Pending mailbox timeout, exiting.\n"); + rval = QLA_FUNCTION_TIMEOUT; + goto premature_exit; + } + WRT_REG_DWORD(®->isp82.hint, HINT_MBX_INT_PENDING); + } else if (IS_FWI2_CAPABLE(ha)) WRT_REG_DWORD(®->isp24.hccr, HCCRX_SET_HOST_INT); else WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); @@ -135,10 +181,22 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); } else { - DEBUG3_11(printk("%s(%ld): cmd=%x POLLING MODE.\n", __func__, - ha->host_no, command)); + ql_dbg(ql_dbg_mbx, vha, 0x1011, + "Cmd=%x Polling Mode.\n", command); - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA82XX(ha)) { + if (RD_REG_DWORD(®->isp82.hint) & + HINT_MBX_INT_PENDING) { + spin_unlock_irqrestore(&ha->hardware_lock, + flags); + ha->flags.mbox_busy = 0; + ql_dbg(ql_dbg_mbx, vha, 0x1012, + "Pending mailbox timeout, exiting.\n"); + rval = QLA_FUNCTION_TIMEOUT; + goto premature_exit; + } + WRT_REG_DWORD(®->isp82.hint, HINT_MBX_INT_PENDING); + } else if (IS_FWI2_CAPABLE(ha)) WRT_REG_DWORD(®->isp24.hccr, HCCRX_SET_HOST_INT); else WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); @@ -150,25 +208,40 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) break; /* Check for pending interrupts. */ - qla2x00_poll(ha); + qla2x00_poll(ha->rsp_q_map[0]); - if (command != MBC_LOAD_RISC_RAM_EXTENDED && - !ha->flags.mbox_int) + if (!ha->flags.mbox_int && + !(IS_QLA2200(ha) && + command == MBC_LOAD_RISC_RAM_EXTENDED)) msleep(10); } /* while */ + ql_dbg(ql_dbg_mbx, vha, 0x1013, + "Waited %d sec.\n", + (uint)((jiffies - (wait_time - (mcp->tov * HZ)))/HZ)); } /* Check whether we timed out */ if (ha->flags.mbox_int) { uint16_t *iptr2; - DEBUG3_11(printk("%s(%ld): cmd %x completed.\n", __func__, - ha->host_no, command)); + ql_dbg(ql_dbg_mbx, vha, 0x1014, + "Cmd=%x completed.\n", command); /* Got interrupt. Clear the flag. */ ha->flags.mbox_int = 0; clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + if (ha->flags.isp82xx_fw_hung) { + ha->flags.mbox_busy = 0; + /* Setting Link-Down error */ + mcp->mb[0] = MBS_LINK_DOWN_ERROR; + ha->mcp = NULL; + rval = QLA_FUNCTION_FAILED; + ql_log(ql_log_warn, vha, 0x1015, + "FW hung = %d.\n", ha->flags.isp82xx_fw_hung); + goto premature_exit; + } + if (ha->mailbox_out[0] != MBS_COMMAND_COMPLETE) rval = QLA_FUNCTION_FAILED; @@ -186,8 +259,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) } } else { -#if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3) || \ - defined(QL_DEBUG_LEVEL_11) uint16_t mb0; uint32_t ictrl; @@ -198,14 +269,16 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) mb0 = RD_MAILBOX_REG(ha, ®->isp, 0); ictrl = RD_REG_WORD(®->isp.ictrl); } - printk("%s(%ld): **** MB Command Timeout for cmd %x ****\n", - __func__, ha->host_no, command); - printk("%s(%ld): icontrol=%x jiffies=%lx\n", __func__, - ha->host_no, ictrl, jiffies); - printk("%s(%ld): *** mailbox[0] = 0x%x ***\n", __func__, - ha->host_no, mb0); - qla2x00_dump_regs(ha); -#endif + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119, + "MBX Command timeout for cmd %x, iocontrol=%x jiffies=%lx " + "mb[0]=0x%x\n", command, ictrl, jiffies, mb0); + ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1019); + + /* + * Attempt to capture a firmware dump for further analysis + * of the current firmware state + */ + ha->isp_ops->fw_dump(vha, 0); rval = QLA_FUNCTION_TIMEOUT; } @@ -215,74 +288,107 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) /* Clean up */ ha->mcp = NULL; - if (abort_active || !io_lock_on) { - DEBUG11(printk("%s(%ld): checking for additional resp " - "interrupt.\n", __func__, ha->host_no)); + if ((abort_active || !io_lock_on) && !IS_NOPOLLING_TYPE(ha)) { + ql_dbg(ql_dbg_mbx, vha, 0x101a, + "Checking for additional resp interrupt.\n"); /* polling mode for non isp_abort commands. */ - qla2x00_poll(ha); + qla2x00_poll(ha->rsp_q_map[0]); } if (rval == QLA_FUNCTION_TIMEOUT && mcp->mb[0] != MBC_GEN_SYSTEM_ERROR) { - if (!io_lock_on || (mcp->flags & IOCTL_CMD)) { + if (!io_lock_on || (mcp->flags & IOCTL_CMD) || + ha->flags.eeh_busy) { /* not in dpc. schedule it for dpc to take over. */ - DEBUG(printk("%s(%ld): timeout schedule " - "isp_abort_needed.\n", __func__, ha->host_no)); - DEBUG2_3_11(printk("%s(%ld): timeout schedule " - "isp_abort_needed.\n", __func__, ha->host_no)); - qla_printk(KERN_WARNING, ha, - "Mailbox command timeout occurred. Scheduling ISP " - "abort.\n"); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); + ql_dbg(ql_dbg_mbx, vha, 0x101b, + "Timeout, schedule isp_abort_needed.\n"); + + if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) && + !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) && + !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { + if (IS_QLA82XX(ha)) { + ql_dbg(ql_dbg_mbx, vha, 0x112a, + "disabling pause transmit on port " + "0 & 1.\n"); + qla82xx_wr_32(ha, + QLA82XX_CRB_NIU + 0x98, + CRB_NIU_XG_PAUSE_CTL_P0| + CRB_NIU_XG_PAUSE_CTL_P1); + } + ql_log(ql_log_info, base_vha, 0x101c, + "Mailbox cmd timeout occurred, cmd=0x%x, " + "mb[0]=0x%x, eeh_busy=0x%x. Scheduling ISP " + "abort.\n", command, mcp->mb[0], + ha->flags.eeh_busy); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } } else if (!abort_active) { /* call abort directly since we are in the DPC thread */ - DEBUG(printk("%s(%ld): timeout calling abort_isp\n", - __func__, ha->host_no)); - DEBUG2_3_11(printk("%s(%ld): timeout calling " - "abort_isp\n", __func__, ha->host_no)); - qla_printk(KERN_WARNING, ha, - "Mailbox command timeout occurred. Issuing ISP " - "abort.\n"); + ql_dbg(ql_dbg_mbx, vha, 0x101d, + "Timeout, calling abort_isp.\n"); - set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); - clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - if (qla2x00_abort_isp(ha)) { - /* Failed. retry later. */ - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) && + !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) && + !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { + if (IS_QLA82XX(ha)) { + ql_dbg(ql_dbg_mbx, vha, 0x112b, + "disabling pause transmit on port " + "0 & 1.\n"); + qla82xx_wr_32(ha, + QLA82XX_CRB_NIU + 0x98, + CRB_NIU_XG_PAUSE_CTL_P0| + CRB_NIU_XG_PAUSE_CTL_P1); + } + ql_log(ql_log_info, base_vha, 0x101e, + "Mailbox cmd timeout occurred, cmd=0x%x, " + "mb[0]=0x%x. Scheduling ISP abort ", + command, mcp->mb[0]); + set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags); + clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + /* Allow next mbx cmd to come in. */ + complete(&ha->mbx_cmd_comp); + if (ha->isp_ops->abort_isp(vha)) { + /* Failed. retry later. */ + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); + } + clear_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags); + ql_dbg(ql_dbg_mbx, vha, 0x101f, + "Finished abort_isp.\n"); + goto mbx_done; } - clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); - DEBUG(printk("%s(%ld): finished abort_isp\n", __func__, - ha->host_no)); - DEBUG2_3_11(printk("%s(%ld): finished abort_isp\n", - __func__, ha->host_no)); } } +premature_exit: /* Allow next mbx cmd to come in. */ complete(&ha->mbx_cmd_comp); +mbx_done: if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, " - "mbx2=%x, cmd=%x ****\n", __func__, ha->host_no, - mcp->mb[0], mcp->mb[1], mcp->mb[2], command)); + ql_dbg(ql_dbg_mbx, base_vha, 0x1020, + "**** Failed mbx[0]=%x, mb[1]=%x, mb[2]=%x, mb[3]=%x, cmd=%x ****.\n", + mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], command); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx, base_vha, 0x1021, "Done %s.\n", __func__); } return rval; } int -qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr, +qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr, uint32_t risc_code_size) { int rval; + struct qla_hw_data *ha = vha->hw; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1022, + "Entered %s.\n", __func__); if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) { mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED; @@ -310,18 +416,20 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr, mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - ha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1023, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1024, + "Done %s.\n", __func__); } return rval; } +#define EXTENDED_BB_CREDITS BIT_0 /* * qla2x00_execute_fw * Start adapter firmware. @@ -338,13 +446,15 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr, * Kernel context. */ int -qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr) +qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) { int rval; + struct qla_hw_data *ha = vha->hw; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1025, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_EXECUTE_FIRMWARE; mcp->out_mb = MBX_0; @@ -353,7 +463,12 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr) mcp->mb[1] = MSW(risc_addr); mcp->mb[2] = LSW(risc_addr); mcp->mb[3] = 0; - mcp->mb[4] = 0; + if (IS_QLA81XX(ha) || IS_QLA83XX(ha)) { + struct nvram_81xx *nv = ha->nvram; + mcp->mb[4] = (nv->enhanced_features & + EXTENDED_BB_CREDITS); + } else + mcp->mb[4] = 0; mcp->out_mb |= MBX_4|MBX_3|MBX_2|MBX_1; mcp->in_mb |= MBX_1; } else { @@ -367,18 +482,18 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr) mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - ha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1026, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { if (IS_FWI2_CAPABLE(ha)) { - DEBUG11(printk("%s(%ld): done exchanges=%x.\n", - __func__, ha->host_no, mcp->mb[1])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1027, + "Done exchanges=%x.\n", mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1028, + "Done %s.\n", __func__); } } @@ -401,41 +516,72 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr) * Context: * Kernel context. */ -void -qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor, - uint16_t *subminor, uint16_t *attributes, uint32_t *memory) +int +qla2x00_get_fw_version(scsi_qla_host_t *vha) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1029, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_FIRMWARE_VERSION; mcp->out_mb = MBX_0; mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + if (IS_QLA81XX(vha->hw) || IS_QLA8031(ha)) + mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8; + if (IS_QLA83XX(vha->hw)) + mcp->in_mb |= MBX_17|MBX_16|MBX_15; mcp->flags = 0; mcp->tov = MBX_TOV_SECONDS; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) + goto failed; /* Return mailbox data. */ - *major = mcp->mb[1]; - *minor = mcp->mb[2]; - *subminor = mcp->mb[3]; - *attributes = mcp->mb[6]; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) - *memory = 0x1FFFF; /* Defaults to 128KB. */ + ha->fw_major_version = mcp->mb[1]; + ha->fw_minor_version = mcp->mb[2]; + ha->fw_subminor_version = mcp->mb[3]; + ha->fw_attributes = mcp->mb[6]; + if (IS_QLA2100(vha->hw) || IS_QLA2200(vha->hw)) + ha->fw_memory_size = 0x1FFFF; /* Defaults to 128KB. */ else - *memory = (mcp->mb[5] << 16) | mcp->mb[4]; + ha->fw_memory_size = (mcp->mb[5] << 16) | mcp->mb[4]; + if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw)) { + ha->mpi_version[0] = mcp->mb[10] & 0xff; + ha->mpi_version[1] = mcp->mb[11] >> 8; + ha->mpi_version[2] = mcp->mb[11] & 0xff; + ha->mpi_capabilities = (mcp->mb[12] << 16) | mcp->mb[13]; + ha->phy_version[0] = mcp->mb[8] & 0xff; + ha->phy_version[1] = mcp->mb[9] >> 8; + ha->phy_version[2] = mcp->mb[9] & 0xff; + } + if (IS_QLA83XX(ha)) { + if (mcp->mb[6] & BIT_15) { + ha->fw_attributes_h = mcp->mb[15]; + ha->fw_attributes_ext[0] = mcp->mb[16]; + ha->fw_attributes_ext[1] = mcp->mb[17]; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1139, + "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n", + __func__, mcp->mb[15], mcp->mb[6]); + } else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x112f, + "%s: FwAttributes [Upper] invalid, MB6:%04x\n", + __func__, mcp->mb[6]); + } +failed: if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x102a, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102b, + "Done %s.\n", __func__); } + return rval; } /* @@ -453,32 +599,33 @@ qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor, * Kernel context. */ int -qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts) +qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102c, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_FIRMWARE_OPTION; mcp->out_mb = MBX_0; mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x102d, "Failed=%x.\n", rval); } else { fwopts[0] = mcp->mb[0]; fwopts[1] = mcp->mb[1]; fwopts[2] = mcp->mb[2]; fwopts[3] = mcp->mb[3]; - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102e, + "Done %s.\n", __func__); } return rval; @@ -500,13 +647,14 @@ qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts) * Kernel context. */ int -qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts) +qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102f, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_SET_FIRMWARE_OPTION; mcp->mb[1] = fwopts[1]; @@ -514,7 +662,7 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts) mcp->mb[3] = fwopts[3]; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_0; - if (IS_FWI2_CAPABLE(ha)) { + if (IS_FWI2_CAPABLE(vha->hw)) { mcp->in_mb |= MBX_1; } else { mcp->mb[10] = fwopts[10]; @@ -524,17 +672,18 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts) } mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); fwopts[0] = mcp->mb[0]; if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x/%x).\n", __func__, - ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1030, + "Failed=%x (%x/%x).\n", rval, mcp->mb[0], mcp->mb[1]); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1031, + "Done %s.\n", __func__); } return rval; @@ -556,13 +705,14 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts) * Kernel context. */ int -qla2x00_mbx_reg_test(scsi_qla_host_t *ha) +qla2x00_mbx_reg_test(scsi_qla_host_t *vha) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1032, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST; mcp->mb[1] = 0xAAAA; @@ -576,7 +726,7 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha) mcp->in_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval == QLA_SUCCESS) { if (mcp->mb[1] != 0xAAAA || mcp->mb[2] != 0x5555 || @@ -585,24 +735,15 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha) if (mcp->mb[5] != 0xA5A5 || mcp->mb[6] != 0x5A5A || mcp->mb[7] != 0x2525) rval = QLA_FUNCTION_FAILED; - if (rval == QLA_FUNCTION_FAILED) { - struct device_reg_24xx __iomem *reg = - &ha->iobase->isp24; - - qla2xxx_hw_event_log(ha, HW_EVENT_ISP_ERR, 0, - LSW(RD_REG_DWORD(®->hccr)), - LSW(RD_REG_DWORD(®->istatus))); - } } if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_mbx_reg_test(%ld): failed=%x.\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_mbx_reg_test(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1034, + "Done %s.\n", __func__); } return rval; @@ -624,18 +765,19 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha) * Kernel context. */ int -qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr) +qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1035, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_VERIFY_CHECKSUM; mcp->out_mb = MBX_0; mcp->in_mb = MBX_0; - if (IS_FWI2_CAPABLE(ha)) { + if (IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[1] = MSW(risc_addr); mcp->mb[2] = LSW(risc_addr); mcp->out_mb |= MBX_2|MBX_1; @@ -648,14 +790,15 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr) mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x chk sum=%x.\n", __func__, - ha->host_no, rval, IS_FWI2_CAPABLE(ha) ? - (mcp->mb[2] << 16) | mcp->mb[1]: mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1036, + "Failed=%x chm sum=%x.\n", rval, IS_FWI2_CAPABLE(vha->hw) ? + (mcp->mb[2] << 16) | mcp->mb[1] : mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1037, + "Done %s.\n", __func__); } return rval; @@ -679,14 +822,17 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr) * Context: * Kernel context. */ -static int -qla2x00_issue_iocb_timeout(scsi_qla_host_t *ha, void *buffer, +int +qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr, size_t size, uint32_t tov) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1038, + "Entered %s.\n", __func__); + mcp->mb[0] = MBC_IOCB_COMMAND_A64; mcp->mb[1] = 0; mcp->mb[2] = MSW(phys_addr); @@ -697,30 +843,29 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *ha, void *buffer, mcp->in_mb = MBX_2|MBX_0; mcp->tov = tov; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n", - ha->host_no, rval)); - DEBUG2(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1039, "Failed=%x.\n", rval); } else { sts_entry_t *sts_entry = (sts_entry_t *) buffer; /* Mask reserved bits. */ sts_entry->entry_status &= - IS_FWI2_CAPABLE(ha) ? RF_MASK_24XX :RF_MASK; + IS_FWI2_CAPABLE(vha->hw) ? RF_MASK_24XX : RF_MASK; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103a, + "Done %s.\n", __func__); } return rval; } int -qla2x00_issue_iocb(scsi_qla_host_t *ha, void *buffer, dma_addr_t phys_addr, +qla2x00_issue_iocb(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr, size_t size) { - return qla2x00_issue_iocb_timeout(ha, buffer, phys_addr, size, + return qla2x00_issue_iocb_timeout(vha, buffer, phys_addr, size, MBX_TOV_SECONDS); } @@ -739,26 +884,28 @@ qla2x00_issue_iocb(scsi_qla_host_t *ha, void *buffer, dma_addr_t phys_addr, * Kernel context. */ int -qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp) +qla2x00_abort_command(srb_t *sp) { unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); - fc_port_t *fcport; int rval; - uint32_t handle; + uint32_t handle = 0; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + fc_port_t *fcport = sp->fcport; + scsi_qla_host_t *vha = fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = vha->req; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); - DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103b, + "Entered %s.\n", __func__); - fcport = sp->fcport; - - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) { - if (ha->outstanding_cmds[handle] == sp) + if (req->outstanding_cmds[handle] == sp) break; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); if (handle == MAX_OUTSTANDING_COMMANDS) { /* command not found */ @@ -772,106 +919,120 @@ qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp) mcp->mb[1] = fcport->loop_id << 8; mcp->mb[2] = (uint16_t)handle; mcp->mb[3] = (uint16_t)(handle >> 16); - mcp->mb[6] = (uint16_t)sp->cmd->device->lun; + mcp->mb[6] = (uint16_t)cmd->device->lun; mcp->out_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("qla2x00_abort_command(%ld): failed=%x.\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x103c, "Failed=%x.\n", rval); } else { - DEBUG11(printk("qla2x00_abort_command(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103d, + "Done %s.\n", __func__); } return rval; } int -qla2x00_abort_target(struct fc_port *fcport, unsigned int l) +qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag) { int rval, rval2; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - scsi_qla_host_t *ha; - - DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no)); + scsi_qla_host_t *vha; + struct req_que *req; + struct rsp_que *rsp; l = l; - ha = fcport->ha; + vha = fcport->vha; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103e, + "Entered %s.\n", __func__); + + req = vha->hw->req_q_map[0]; + rsp = req->rsp; mcp->mb[0] = MBC_ABORT_TARGET; mcp->out_mb = MBX_9|MBX_2|MBX_1|MBX_0; - if (HAS_EXTENDED_IDS(ha)) { + if (HAS_EXTENDED_IDS(vha->hw)) { mcp->mb[1] = fcport->loop_id; mcp->mb[10] = 0; mcp->out_mb |= MBX_10; } else { mcp->mb[1] = fcport->loop_id << 8; } - mcp->mb[2] = ha->loop_reset_delay; - mcp->mb[9] = ha->vp_idx; + mcp->mb[2] = vha->hw->loop_reset_delay; + mcp->mb[9] = vha->vp_idx; mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103f, + "Failed=%x.\n", rval); } /* Issue marker IOCB. */ - rval2 = qla2x00_marker(ha, fcport->loop_id, 0, MK_SYNC_ID); + rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, 0, + MK_SYNC_ID); if (rval2 != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB " - "(%x).\n", __func__, ha->host_no, rval2)); + ql_dbg(ql_dbg_mbx, vha, 0x1040, + "Failed to issue marker IOCB (%x).\n", rval2); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1041, + "Done %s.\n", __func__); } return rval; } int -qla2x00_lun_reset(struct fc_port *fcport, unsigned int l) +qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag) { int rval, rval2; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - scsi_qla_host_t *ha; + scsi_qla_host_t *vha; + struct req_que *req; + struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no)); + vha = fcport->vha; - ha = fcport->ha; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1042, + "Entered %s.\n", __func__); + + req = vha->hw->req_q_map[0]; + rsp = req->rsp; mcp->mb[0] = MBC_LUN_RESET; mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0; - if (HAS_EXTENDED_IDS(ha)) + if (HAS_EXTENDED_IDS(vha->hw)) mcp->mb[1] = fcport->loop_id; else mcp->mb[1] = fcport->loop_id << 8; mcp->mb[2] = l; mcp->mb[3] = 0; - mcp->mb[9] = ha->vp_idx; + mcp->mb[9] = vha->vp_idx; mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1043, "Failed=%x.\n", rval); } /* Issue marker IOCB. */ - rval2 = qla2x00_marker(ha, fcport->loop_id, l, MK_SYNC_ID_LUN); + rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l, + MK_SYNC_ID_LUN); if (rval2 != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB " - "(%x).\n", __func__, ha->host_no, rval2)); + ql_dbg(ql_dbg_mbx, vha, 0x1044, + "Failed to issue marker IOCB (%x).\n", rval2); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1045, + "Done %s.\n", __func__); } return rval; @@ -898,25 +1059,29 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l) * Kernel context. */ int -qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, +qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, uint8_t *area, uint8_t *domain, uint16_t *top, uint16_t *sw_cap) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_adapter_id(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1046, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID; - mcp->mb[9] = ha->vp_idx; + mcp->mb[9] = vha->vp_idx; mcp->out_mb = MBX_9|MBX_0; mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + if (IS_CNA_CAPABLE(vha->hw)) + mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (mcp->mb[0] == MBS_COMMAND_ERROR) rval = QLA_COMMAND_ERROR; + else if (mcp->mb[0] == MBS_INVALID_COMMAND) + rval = QLA_INVALID_COMMAND; /* Return data. */ *id = mcp->mb[1]; @@ -928,12 +1093,21 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1047, "Failed=%x.\n", rval); } else { - /*EMPTY*/ - DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1048, + "Done %s.\n", __func__); + + if (IS_CNA_CAPABLE(vha->hw)) { + vha->fcoe_vlan_id = mcp->mb[9] & 0xfff; + vha->fcoe_fcf_idx = mcp->mb[10]; + vha->fcoe_vn_port_mac[5] = mcp->mb[11] >> 8; + vha->fcoe_vn_port_mac[4] = mcp->mb[11] & 0xff; + vha->fcoe_vn_port_mac[3] = mcp->mb[12] >> 8; + vha->fcoe_vn_port_mac[2] = mcp->mb[12] & 0xff; + vha->fcoe_vn_port_mac[1] = mcp->mb[13] >> 8; + vha->fcoe_vn_port_mac[0] = mcp->mb[13] & 0xff; + } } return rval; @@ -955,7 +1129,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, * Kernel context. */ int -qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov, +qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov, uint16_t *r_a_tov) { int rval; @@ -963,20 +1137,20 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_retry_cnt(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1049, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_RETRY_COUNT; mcp->out_mb = MBX_0; mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_retry_cnt(%ld): failed = %x.\n", - ha->host_no, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x104a, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { /* Convert returned data and check our values. */ *r_a_tov = mcp->mb[3] / 2; @@ -987,8 +1161,8 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov, *tov = ratov; } - DEBUG11(printk("qla2x00_get_retry_cnt(%ld): done. mb3=%d " - "ratov=%d.\n", ha->host_no, mcp->mb[3], ratov)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104b, + "Done %s mb3=%d ratov=%d.\n", __func__, mcp->mb[3], ratov); } return rval; @@ -1012,25 +1186,27 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov, * Kernel context. */ int -qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size) +qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104c, + "Entered %s.\n", __func__); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (!qla_tgt_mode_enabled(ha) && !qla_ini_mode_enabled(ha)) { - DEBUG11(printk("qla2x00_init_firmware(%ld): neither initiator, " - "nor target mode enabled, exiting\n", ha->host_no)); + if (!qla_firmware_active(vha)) { + printk("qla2x00_init_firmware(%ld): Neither initiator, " + "nor target mode enabled\n", vha->host_no); rval = QLA_SUCCESS; goto out; } -#endif -#ifdef QL_DEBUG_LEVEL_5 + if (IS_QLA82XX(ha) && ql2xdbwr) + qla82xx_wr_32(ha, ha->nxdb_wr_ptr, + (0x04 | (ha->portnum << 5) | (0 << 8) | (0 << 16))); +#if 0 if (IS_FWI2_CAPABLE(ha)) { struct init_cb_24xx *icb = (struct init_cb_24xx *)ha->init_cb; DEBUG5(printk(KERN_INFO "%s(): firmware_options_1 %x, " @@ -1057,34 +1233,83 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size) else mcp->mb[0] = MBC_INITIALIZE_FIRMWARE; + mcp->mb[1] = 0; mcp->mb[2] = MSW(ha->init_cb_dma); mcp->mb[3] = LSW(ha->init_cb_dma); mcp->mb[4] = 0; mcp->mb[5] = 0; mcp->mb[6] = MSW(MSD(ha->init_cb_dma)); mcp->mb[7] = LSW(MSD(ha->init_cb_dma)); - mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; - mcp->in_mb = MBX_5|MBX_4|MBX_0; + mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + if ((IS_QLA81XX(ha) || IS_QLA83XX(ha)) && ha->ex_init_cb->ex_version) { + mcp->mb[1] = BIT_0; + mcp->mb[10] = MSW(ha->ex_init_cb_dma); + mcp->mb[11] = LSW(ha->ex_init_cb_dma); + mcp->mb[12] = MSW(MSD(ha->ex_init_cb_dma)); + mcp->mb[13] = LSW(MSD(ha->ex_init_cb_dma)); + mcp->mb[14] = sizeof(*ha->ex_init_cb); + mcp->out_mb |= MBX_14|MBX_13|MBX_12|MBX_11|MBX_10; + } + /* 1 and 2 should normally be captured. */ + mcp->in_mb = MBX_2|MBX_1|MBX_0; + if (IS_QLA83XX(ha)) + /* mb3 is additional info about the installed SFP. */ + mcp->in_mb |= MBX_3; mcp->buf_size = size; mcp->flags = MBX_DMA_OUT; mcp->tov = MBX_TOV_SECONDS; - rval = qla2x00_mailbox_command(ha, mcp); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (IS_FWI2_CAPABLE(ha)) { + struct mid_init_cb_24xx *micb = (struct mid_init_cb_24xx *)ha->init_cb; + + memset(micb->entries, 0, sizeof(micb->entries)); + + if (ha->flags.npiv_supported && (ha->num_vhosts > 0)) { + scsi_qla_host_t *vha; + list_for_each_entry(vha, &ha->vp_list, list) { + struct mid_conf_entry_24xx *vpe; + + if (!qla_tgt_mode_enabled(vha) && + !qla_ini_mode_enabled(vha)) + continue; + + if (vha->vp_idx == 0) + continue; + + vpe = &micb->entries[vha->vp_idx-1]; + vpe->options = BIT_3|BIT_4|BIT_5; + + /* Enable target mode */ + if (qla_tgt_mode_enabled(vha)) + vpe->options &= ~BIT_5; + /* Disable ini mode, if requested */ + if (!qla_ini_mode_enabled(vha)) + vpe->options &= ~BIT_4; + memcpy(vpe->node_name, vha->node_name, WWN_SIZE); + memcpy(vpe->port_name, vha->port_name, WWN_SIZE); + } + } + } +#endif + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_init_firmware(%ld): failed=%x " - "mb0=%x.\n", - ha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x104d, + "Failed=%x mb[0]=%x, mb[1]=%x, mb[2]=%x, mb[3]=%x,.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_init_firmware(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104e, + "Done %s.\n", __func__); } - +#ifdef CONFIG_SCSI_QLA2XXX_TARGET out: +#endif return rval; } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET /* * qla2x00_get_node_name_list * Issue get node name list mailbox command, kmalloc() @@ -1092,6 +1317,7 @@ out: * * Input: * ha = adapter state pointer. + * include_initiators = include initiators in the resulting list * out_data = resulting list * out_len = length of the resulting list * @@ -1101,15 +1327,17 @@ out: * Context: * Kernel context. */ -int qla2x00_get_node_name_list(scsi_qla_host_t *ha, +int +qla2x00_get_node_name_list(scsi_qla_host_t *vha, bool include_initiators, void **out_data, int *out_len) { - int rval, left; + struct qla_hw_data *ha = vha->hw; + struct qla_port24_data *list = NULL; + void *pmap; mbx_cmd_t mc; dma_addr_t pmap_dma; - void *pmap; - struct qla_port24_data *list = NULL; ulong dma_size; + int rval, left; BUILD_BUG_ON(sizeof(struct qla_port24_data) < sizeof(struct qla_port23_data)); @@ -1118,16 +1346,18 @@ int qla2x00_get_node_name_list(scsi_qla_host_t *ha, while (left > 0) { dma_size = left * sizeof(*list); pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size, - &pmap_dma, GFP_KERNEL); + &pmap_dma, GFP_KERNEL); if (pmap == NULL) { printk(KERN_ERR "%s(%ld): DMA Alloc failed of " - "%ld\n", __func__, ha->host_no, dma_size); + "%ld\n", __func__, vha->host_no, dma_size); rval = QLA_MEMORY_ALLOC_FAILED; goto out; } mc.mb[0] = MBC_PORT_NODE_NAME_LIST; - mc.mb[1] = BIT_1 | BIT_3; + mc.mb[1] = BIT_1; + if (include_initiators) + mc.mb[1] |= BIT_3; mc.mb[2] = MSW(pmap_dma); mc.mb[3] = LSW(pmap_dma); mc.mb[6] = MSW(MSD(pmap_dma)); @@ -1138,10 +1368,12 @@ int qla2x00_get_node_name_list(scsi_qla_host_t *ha, mc.tov = 30; mc.flags = MBX_DMA_IN; - rval = qla2x00_mailbox_command(ha, &mc); + rval = qla2x00_mailbox_command(vha, &mc); if (rval != QLA_SUCCESS) { if ((mc.mb[0] == MBS_COMMAND_ERROR) && (mc.mb[1] == 0xA)) { + ql_dbg(ql_dbg_mbx, vha, 0x11020, + "Not enough buffer, increasing...\n"); if (IS_FWI2_CAPABLE(ha)) left += le16_to_cpu(mc.mb[2]) / sizeof(struct qla_port24_data); else @@ -1153,10 +1385,10 @@ int qla2x00_get_node_name_list(scsi_qla_host_t *ha, left = 0; - list = kmalloc(dma_size, GFP_KERNEL); + list = kzalloc(dma_size, GFP_KERNEL); if (list == NULL) { printk(KERN_ERR "%s(%ld): failed to allocate node names" - " list structure.\n", __func__, ha->host_no); + " list structure.\n", __func__, vha->host_no); rval = QLA_MEMORY_ALLOC_FAILED; goto out_free; } @@ -1174,9 +1406,10 @@ out: out_free: dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); - goto out; + return rval; } EXPORT_SYMBOL(qla2x00_get_node_name_list); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ /* * qla2x00_get_port_database @@ -1195,7 +1428,7 @@ EXPORT_SYMBOL(qla2x00_get_node_name_list); * Kernel context. */ int -qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) +qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt) { int rval; mbx_cmd_t mc; @@ -1203,14 +1436,16 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) port_database_t *pd; struct port_database_24xx *pd24; dma_addr_t pd_dma; + struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104f, + "Entered %s.\n", __func__); pd24 = NULL; pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma); if (pd == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Port Database " - "structure.\n", __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x1050, + "Failed to allocate port database structure.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE)); @@ -1222,7 +1457,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) mcp->mb[3] = LSW(pd_dma); mcp->mb[6] = MSW(MSD(pd_dma)); mcp->mb[7] = LSW(MSD(pd_dma)); - mcp->mb[9] = ha->vp_idx; + mcp->mb[9] = vha->vp_idx; mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_0; if (IS_FWI2_CAPABLE(ha)) { @@ -1242,25 +1477,33 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) PORT_DATABASE_24XX_SIZE : PORT_DATABASE_SIZE; mcp->flags = MBX_DMA_IN; mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2); - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) goto gpd_error_out; if (IS_FWI2_CAPABLE(ha)) { + uint64_t zero = 0; pd24 = (struct port_database_24xx *) pd; /* Check for logged in state. */ if (pd24->current_login_state != PDS_PRLI_COMPLETE && pd24->last_login_state != PDS_PRLI_COMPLETE) { - DEBUG2(printk("%s(%ld): Unable to verify " - "login-state (%x/%x) for loop_id %x\n", - __func__, ha->host_no, - pd24->current_login_state, - pd24->last_login_state, fcport->loop_id)); + ql_dbg(ql_dbg_mbx, vha, 0x1051, + "Unable to verify login-state (%x/%x) for " + "loop_id %x.\n", pd24->current_login_state, + pd24->last_login_state, fcport->loop_id); rval = QLA_FUNCTION_FAILED; goto gpd_error_out; } + if (fcport->loop_id == FC_NO_LOOP_ID || + (memcmp(fcport->port_name, (uint8_t *)&zero, 8) && + memcmp(fcport->port_name, pd24->port_name, 8))) { + /* We lost the device mid way. */ + rval = QLA_NOT_LOGGED_IN; + goto gpd_error_out; + } + /* Names are little-endian. */ memcpy(fcport->node_name, pd24->node_name, WWN_SIZE); memcpy(fcport->port_name, pd24->port_name, WWN_SIZE); @@ -1283,16 +1526,33 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) fcport->supported_classes = (pd24->flags & PDF_CLASS_2) ? FC_COS_CLASS2 : FC_COS_CLASS3; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET if (pd24->prli_svc_param_word_3[0] & BIT_7) fcport->conf_compl_supported = 1; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ } else { + uint64_t zero = 0; + /* Check for logged in state. */ if (pd->master_state != PD_STATE_PORT_LOGGED_IN && pd->slave_state != PD_STATE_PORT_LOGGED_IN) { + ql_dbg(ql_dbg_mbx, vha, 0x100a, + "Unable to verify login-state (%x/%x) - " + "portid=%02x%02x%02x.\n", pd->master_state, + pd->slave_state, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); rval = QLA_FUNCTION_FAILED; goto gpd_error_out; } + if (fcport->loop_id == FC_NO_LOOP_ID || + (memcmp(fcport->port_name, (uint8_t *)&zero, 8) && + memcmp(fcport->port_name, pd->port_name, 8))) { + /* We lost the device mid way. */ + rval = QLA_NOT_LOGGED_IN; + goto gpd_error_out; + } + /* Names are little-endian. */ memcpy(fcport->node_name, pd->node_name, WWN_SIZE); memcpy(fcport->port_name, pd->port_name, WWN_SIZE); @@ -1317,20 +1577,24 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) /* Passback COS information. */ fcport->supported_classes = (pd->options & BIT_4) ? - FC_COS_CLASS2: FC_COS_CLASS3; + FC_COS_CLASS2 : FC_COS_CLASS3; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET if (pd->prli_svc_param_word_3[0] & BIT_7) fcport->conf_compl_supported = 1; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ } gpd_error_out: dma_pool_free(ha->s_dma_pool, pd, pd_dma); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1052, + "Failed=%x mb[0]=%x mb[1]=%x.\n", rval, + mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1053, + "Done %s.\n", __func__); } return rval; @@ -1354,35 +1618,41 @@ EXPORT_SYMBOL(qla2x00_get_port_database); * Kernel context. */ int -qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states) +qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_firmware_state(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1054, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_FIRMWARE_STATE; mcp->out_mb = MBX_0; - mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0; + if (IS_FWI2_CAPABLE(vha->hw)) + mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + else + mcp->in_mb = MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); /* Return firmware states. */ states[0] = mcp->mb[1]; - states[1] = mcp->mb[2]; - states[2] = mcp->mb[3]; + if (IS_FWI2_CAPABLE(vha->hw)) { + states[1] = mcp->mb[2]; + states[2] = mcp->mb[3]; + states[3] = mcp->mb[4]; + states[4] = mcp->mb[5]; + } if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_firmware_state(%ld): " - "failed=%x.\n", ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_get_firmware_state(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1056, + "Done %s.\n", __func__); } return rval; @@ -1407,20 +1677,20 @@ qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states) * Kernel context. */ int -qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name, +qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name, uint8_t opt) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_port_name(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1057, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_PORT_NAME; - mcp->mb[9] = ha->vp_idx; + mcp->mb[9] = vha->vp_idx; mcp->out_mb = MBX_9|MBX_1|MBX_0; - if (HAS_EXTENDED_IDS(ha)) { + if (HAS_EXTENDED_IDS(vha->hw)) { mcp->mb[1] = loop_id; mcp->mb[10] = opt; mcp->out_mb |= MBX_10; @@ -1431,12 +1701,11 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name, mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_port_name(%ld): failed=%x.\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1058, "Failed=%x.\n", rval); } else { if (name != NULL) { /* This function returns name in big endian. */ @@ -1450,8 +1719,8 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name, name[7] = LSB(mcp->mb[7]); } - DEBUG11(printk("qla2x00_get_port_name(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1059, + "Done %s.\n", __func__); } return rval; @@ -1473,45 +1742,52 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name, * Kernel context. */ int -qla2x00_lip_reset(scsi_qla_host_t *ha) +qla2x00_lip_reset(scsi_qla_host_t *vha) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105a, + "Entered %s.\n", __func__); - if (IS_FWI2_CAPABLE(ha)) { + if (IS_CNA_CAPABLE(vha->hw)) { + /* Logout across all FCFs. */ + mcp->mb[0] = MBC_LIP_FULL_LOGIN; + mcp->mb[1] = BIT_1; + mcp->mb[2] = 0; + mcp->out_mb = MBX_2|MBX_1|MBX_0; + } else if (IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[0] = MBC_LIP_FULL_LOGIN; mcp->mb[1] = BIT_6; mcp->mb[2] = 0; - mcp->mb[3] = ha->loop_reset_delay; + mcp->mb[3] = vha->hw->loop_reset_delay; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; } else { mcp->mb[0] = MBC_LIP_RESET; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; - if (HAS_EXTENDED_IDS(ha)) { + if (HAS_EXTENDED_IDS(vha->hw)) { mcp->mb[1] = 0x00ff; mcp->mb[10] = 0; mcp->out_mb |= MBX_10; } else { mcp->mb[1] = 0xff00; } - mcp->mb[2] = ha->loop_reset_delay; + mcp->mb[2] = vha->hw->loop_reset_delay; mcp->mb[3] = 0; } mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", - __func__, ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x105b, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105c, + "Done %s.\n", __func__); } return rval; @@ -1536,18 +1812,19 @@ qla2x00_lip_reset(scsi_qla_host_t *ha) * Kernel context. */ int -qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address, +qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address, uint16_t cmd_size, size_t buf_size) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_send_sns(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105d, + "Entered %s.\n", __func__); - DEBUG11(printk("qla2x00_send_sns: retry cnt=%d ratov=%d total " - "tov=%d.\n", ha->retry_count, ha->login_timeout, mcp->tov)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105e, + "Retry cnt=%d ratov=%d total tov=%d.\n", + vha->hw->retry_count, vha->hw->login_timeout, mcp->tov); mcp->mb[0] = MBC_SEND_SNS_COMMAND; mcp->mb[1] = cmd_size; @@ -1559,25 +1836,25 @@ qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address, mcp->in_mb = MBX_0|MBX_1; mcp->buf_size = buf_size; mcp->flags = MBX_DMA_OUT|MBX_DMA_IN; - mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2); - rval = qla2x00_mailbox_command(ha, mcp); + mcp->tov = (vha->hw->login_timeout * 2) + (vha->hw->login_timeout / 2); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x " - "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1])); - DEBUG2_3_11(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x " - "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x105f, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1060, + "Done %s.\n", __func__); } return rval; } int -qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, +qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt) { int rval; @@ -1585,19 +1862,30 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, struct logio_entry_24xx *lg; dma_addr_t lg_dma; uint32_t iop[2]; + struct qla_hw_data *ha = vha->hw; + struct req_que *req; + struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1061, + "Entered %s.\n", __func__); + + if (ha->flags.cpu_affinity_enabled) + req = ha->req_q_map[0]; + else + req = vha->req; + rsp = req->rsp; lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); if (lg == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Login IOCB.\n", - __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x1062, + "Failed to allocate login IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(lg, 0, sizeof(struct logio_entry_24xx)); lg->entry_type = LOGINOUT_PORT_IOCB_TYPE; lg->entry_count = 1; + lg->handle = MAKE_HANDLE(req->id, lg->handle); lg->nport_handle = cpu_to_le16(loop_id); lg->control_flags = __constant_cpu_to_le16(LCF_COMMAND_PLOGI); if (opt & BIT_0) @@ -1607,24 +1895,25 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; - lg->vp_index = ha->vp_idx; - rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0); + lg->vp_index = vha->vp_idx; + rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0, + (ha->r_a_tov / 10 * 2) + 2); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB " - "(%x).\n", __func__, ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1063, + "Failed to issue login IOCB (%x).\n", rval); } else if (lg->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, ha->host_no, - lg->entry_status)); + ql_dbg(ql_dbg_mbx, vha, 0x1064, + "Failed to complete IOCB -- error status (%x).\n", + lg->entry_status); rval = QLA_FUNCTION_FAILED; } else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { iop[0] = le32_to_cpu(lg->io_parameter[0]); iop[1] = le32_to_cpu(lg->io_parameter[1]); - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x) ioparam=%x/%x.\n", __func__, - ha->host_no, le16_to_cpu(lg->comp_status), iop[0], - iop[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1065, + "Failed to complete IOCB -- completion status (%x) " + "ioparam=%x/%x.\n", le16_to_cpu(lg->comp_status), + iop[0], iop[1]); switch (iop[0]) { case LSC_SCODE_PORTID_USED: @@ -1652,7 +1941,8 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, break; } } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1066, + "Done %s.\n", __func__); iop[0] = le32_to_cpu(lg->io_parameter[0]); @@ -1701,14 +1991,16 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, * Kernel context. */ int -qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, +qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_LOGIN_FABRIC_PORT; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; @@ -1725,7 +2017,7 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, mcp->in_mb = MBX_7|MBX_6|MBX_2|MBX_1|MBX_0; mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2); mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); /* Return mailbox statuses. */ if (mb != NULL) { @@ -1751,13 +2043,13 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, rval = QLA_SUCCESS; /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_login_fabric(%ld): failed=%x " - "mb[0]=%x mb[1]=%x mb[2]=%x.\n", ha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[2])); + ql_dbg(ql_dbg_mbx, vha, 0x1068, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_login_fabric(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069, + "Done %s.\n", __func__); } return rval; @@ -1780,20 +2072,22 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, * */ int -qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport, +qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t *mb_ret, uint8_t opt) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106a, + "Entered %s.\n", __func__); if (IS_FWI2_CAPABLE(ha)) - return qla24xx_login_fabric(ha, fcport->loop_id, + return qla24xx_login_fabric(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, mb_ret, opt); - DEBUG3(printk("%s(%ld): entered.\n", __func__, ha->host_no)); - mcp->mb[0] = MBC_LOGIN_LOOP_PORT; if (HAS_EXTENDED_IDS(ha)) mcp->mb[1] = fcport->loop_id; @@ -1804,7 +2098,7 @@ qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport, mcp->in_mb = MBX_7|MBX_6|MBX_1|MBX_0; mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2); mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); /* Return mailbox statuses. */ if (mb_ret != NULL) { @@ -1824,65 +2118,76 @@ qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport, if (mcp->mb[0] == 0x4005 || mcp->mb[0] == 0x4006) rval = QLA_SUCCESS; - DEBUG(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x " - "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7])); - DEBUG2_3(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x " - "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7])); + ql_dbg(ql_dbg_mbx, vha, 0x106b, + "Failed=%x mb[0]=%x mb[1]=%x mb[6]=%x mb[7]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]); } else { /*EMPTY*/ - DEBUG3(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106c, + "Done %s.\n", __func__); } return (rval); } int -qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, +qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, uint8_t area, uint8_t al_pa) { int rval; struct logio_entry_24xx *lg; dma_addr_t lg_dma; + struct qla_hw_data *ha = vha->hw; + struct req_que *req; + struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d, + "Entered %s.\n", __func__); lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); if (lg == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Logout IOCB.\n", - __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x106e, + "Failed to allocate logout IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(lg, 0, sizeof(struct logio_entry_24xx)); + if (ql2xmaxqueues > 1) + req = ha->req_q_map[0]; + else + req = vha->req; + rsp = req->rsp; lg->entry_type = LOGINOUT_PORT_IOCB_TYPE; lg->entry_count = 1; + lg->handle = MAKE_HANDLE(req->id, lg->handle); lg->nport_handle = cpu_to_le16(loop_id); lg->control_flags = - __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); + __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO| + LCF_FREE_NPORT); lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; - lg->vp_index = ha->vp_idx; - rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0); + lg->vp_index = vha->vp_idx; + rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0, + (ha->r_a_tov / 10 * 2) + 2); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB " - "(%x).\n", __func__, ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x106f, + "Failed to issue logout IOCB (%x).\n", rval); } else if (lg->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, ha->host_no, - lg->entry_status)); + ql_dbg(ql_dbg_mbx, vha, 0x1070, + "Failed to complete IOCB -- error status (%x).\n", + lg->entry_status); rval = QLA_FUNCTION_FAILED; } else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x) ioparam=%x/%x.\n", __func__, - ha->host_no, le16_to_cpu(lg->comp_status), + ql_dbg(ql_dbg_mbx, vha, 0x1071, + "Failed to complete IOCB -- completion status (%x) " + "ioparam=%x/%x.\n", le16_to_cpu(lg->comp_status), le32_to_cpu(lg->io_parameter[0]), - le32_to_cpu(lg->io_parameter[1]))); + le32_to_cpu(lg->io_parameter[1])); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1072, + "Done %s.\n", __func__); } dma_pool_free(ha->s_dma_pool, lg, lg_dma); @@ -1907,19 +2212,19 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, * Kernel context. */ int -qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, +qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, uint8_t area, uint8_t al_pa) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_fabric_logout(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1073, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT; mcp->out_mb = MBX_1|MBX_0; - if (HAS_EXTENDED_IDS(ha)) { + if (HAS_EXTENDED_IDS(vha->hw)) { mcp->mb[1] = loop_id; mcp->mb[10] = 0; mcp->out_mb |= MBX_10; @@ -1930,16 +2235,16 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, mcp->in_mb = MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_fabric_logout(%ld): failed=%x " - "mbx1=%x.\n", ha->host_no, rval, mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1074, + "Failed=%x mb[1]=%x.\n", rval, mcp->mb[1]); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_fabric_logout(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1075, + "Done %s.\n", __func__); } return rval; @@ -1961,33 +2266,32 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, * Kernel context. */ int -qla2x00_full_login_lip(scsi_qla_host_t *ha) +qla2x00_full_login_lip(scsi_qla_host_t *vha) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1076, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_LIP_FULL_LOGIN; - mcp->mb[1] = IS_FWI2_CAPABLE(ha) ? BIT_3: 0; + mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0; mcp->mb[2] = 0; mcp->mb[3] = 0; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_full_login_lip(%ld): failed=%x.\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1077, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_full_login_lip(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1078, + "Done %s.\n", __func__); } return rval; @@ -2006,28 +2310,29 @@ qla2x00_full_login_lip(scsi_qla_host_t *ha) * Kernel context. */ int -qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma, +qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma, uint16_t *entries) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("qla2x00_get_id_list(%ld): entered.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1079, + "Entered %s.\n", __func__); if (id_list == NULL) return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_GET_ID_LIST; mcp->out_mb = MBX_0; - if (IS_FWI2_CAPABLE(ha)) { + if (IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[2] = MSW(id_list_dma); mcp->mb[3] = LSW(id_list_dma); mcp->mb[6] = MSW(MSD(id_list_dma)); mcp->mb[7] = LSW(MSD(id_list_dma)); mcp->mb[8] = 0; - mcp->mb[9] = ha->vp_idx; + mcp->mb[9] = vha->vp_idx; mcp->out_mb |= MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2; } else { mcp->mb[1] = MSW(id_list_dma); @@ -2039,18 +2344,17 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma, mcp->in_mb = MBX_1|MBX_0; mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2); mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { if ((mcp->mb[0] == MBS_COMMAND_ERROR) && (mcp->mb[1] == 0x7)) rval = QLA_FW_NOT_READY; - DEBUG2_3_11(printk("qla2x00_get_id_list(%ld): failed=%x.\n", - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x107a, "Failed=%x.\n", rval); } else { *entries = mcp->mb[1]; - DEBUG11(printk("qla2x00_get_id_list(%ld): done.\n", - ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107b, + "Done %s.\n", __func__); } return rval; @@ -2071,32 +2375,36 @@ EXPORT_SYMBOL(qla2x00_get_id_list); * Kernel context. */ int -qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt, +qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt, - uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports) + uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports, uint16_t *max_fcfs) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107c, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_RESOURCE_COUNTS; mcp->out_mb = MBX_0; mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + if (IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) + mcp->in_mb |= MBX_12; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed = %x.\n", __func__, - ha->host_no, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x107d, + "Failed mb[0]=%x.\n", mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x " - "mb7=%x mb10=%x mb11=%x.\n", __func__, ha->host_no, - mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7], - mcp->mb[10], mcp->mb[11])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107e, + "Done %s mb1=%x mb2=%x mb3=%x mb6=%x mb7=%x mb10=%x " + "mb11=%x mb12=%x.\n", __func__, mcp->mb[1], mcp->mb[2], + mcp->mb[3], mcp->mb[6], mcp->mb[7], mcp->mb[10], + mcp->mb[11], mcp->mb[12]); if (cur_xchg_cnt) *cur_xchg_cnt = mcp->mb[3]; @@ -2106,14 +2414,15 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt, *cur_iocb_cnt = mcp->mb[7]; if (orig_iocb_cnt) *orig_iocb_cnt = mcp->mb[10]; - if (max_npiv_vports) + if (vha->hw->flags.npiv_supported && max_npiv_vports) *max_npiv_vports = mcp->mb[11]; + if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) && max_fcfs) + *max_fcfs = mcp->mb[12]; } return (rval); } -#if defined(QL_DEBUG_LEVEL_3) /* * qla2x00_get_fcal_position_map * Get FCAL (LILP) position map using mailbox command @@ -2129,18 +2438,22 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt, * Kernel context. */ int -qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map) +qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; char *pmap; dma_addr_t pmap_dma; + struct qla_hw_data *ha = vha->hw; - pmap = dma_pool_alloc(ha->s_dma_pool, GFP_ATOMIC, &pmap_dma); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107f, + "Entered %s.\n", __func__); + + pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma); if (pmap == NULL) { - DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****", - __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x1080, + "Memory alloc failed.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(pmap, 0, FCAL_MAP_SIZE); @@ -2155,13 +2468,14 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map) mcp->buf_size = FCAL_MAP_SIZE; mcp->flags = MBX_DMA_IN; mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2); - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval == QLA_SUCCESS) { - DEBUG11(printk("%s(%ld): (mb0=%x/mb1=%x) FC/AL Position Map " - "size (%x)\n", __func__, ha->host_no, mcp->mb[0], - mcp->mb[1], (unsigned)pmap[0])); - DEBUG11(qla2x00_dump_buffer(pmap, pmap[0] + 1)); + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1081, + "mb0/mb1=%x/%X FC/AL position map size (%x).\n", + mcp->mb[0], mcp->mb[1], (unsigned)pmap[0]); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111d, + pmap, pmap[0] + 1); if (pos_map) memcpy(pos_map, pmap, FCAL_MAP_SIZE); @@ -2169,15 +2483,14 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map) dma_pool_free(ha->s_dma_pool, pmap, pmap_dma); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1082, "Failed=%x.\n", rval); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1083, + "Done %s.\n", __func__); } return rval; } -#endif /* * qla2x00_get_link_status @@ -2193,15 +2506,17 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map) * BIT_1 = mailbox error. */ int -qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id, +qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id, struct link_statistics *stats, dma_addr_t stats_dma) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; uint32_t *siter, *diter, dwords; + struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1084, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_LINK_STATUS; mcp->mb[2] = MSW(stats_dma); @@ -2226,15 +2541,17 @@ qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id, } mcp->tov = MBX_TOV_SECONDS; mcp->flags = IOCTL_CMD; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval == QLA_SUCCESS) { if (mcp->mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n", - __func__, ha->host_no, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1085, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); rval = QLA_FUNCTION_FAILED; } else { /* Copy over data -- firmware data is LE. */ + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1086, + "Done %s.\n", __func__); dwords = offsetof(struct link_statistics, unused1) / 4; siter = diter = &stats->link_fail_cnt; while (dwords--) @@ -2242,15 +2559,14 @@ qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id, } } else { /* Failed. */ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1087, "Failed=%x.\n", rval); } return rval; } int -qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats, +qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats, dma_addr_t stats_dma) { int rval; @@ -2258,37 +2574,40 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats, mbx_cmd_t *mcp = &mc; uint32_t *siter, *diter, dwords; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1088, + "Entered %s.\n", __func__); + #ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (!qla_tgt_mode_enabled(ha) && !qla_ini_mode_enabled(ha)) { - DEBUG2_3_11(printk("%s(%ld): neither initiator, nor target " + if (!qla_firmware_active(vha)) { + printk("%s(%ld): neither initiator, nor target " "mode enabled, no stats returned\n", - __func__, ha->host_no)); + __func__, vha->host_no); return QLA_FUNCTION_FAILED; } #endif - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); - mcp->mb[0] = MBC_GET_LINK_PRIV_STATS; mcp->mb[2] = MSW(stats_dma); mcp->mb[3] = LSW(stats_dma); mcp->mb[6] = MSW(MSD(stats_dma)); mcp->mb[7] = LSW(MSD(stats_dma)); mcp->mb[8] = sizeof(struct link_statistics) / 4; - mcp->mb[9] = ha->vp_idx; + mcp->mb[9] = vha->vp_idx; mcp->mb[10] = 0; mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = IOCTL_CMD; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval == QLA_SUCCESS) { if (mcp->mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n", - __func__, ha->host_no, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1089, + "Failed mb[0]=%x.\n", mcp->mb[0]); rval = QLA_FUNCTION_FAILED; } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108a, + "Done %s.\n", __func__); /* Copy over data -- firmware data is LE. */ dwords = sizeof(struct link_statistics) / 4; siter = diter = &stats->link_fail_cnt; @@ -2297,34 +2616,35 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats, } } else { /* Failed. */ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x108b, "Failed=%x.\n", rval); } return rval; } int -qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp) +qla24xx_abort_command(srb_t *sp) { int rval; - fc_port_t *fcport; unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct abort_entry_24xx *abt; dma_addr_t abt_dma; uint32_t handle; + fc_port_t *fcport = sp->fcport; + struct scsi_qla_host *vha = fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = vha->req; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108c, + "Entered %s.\n", __func__); - fcport = sp->fcport; - - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) { - if (ha->outstanding_cmds[handle] == sp) + if (req->outstanding_cmds[handle] == sp) break; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); if (handle == MAX_OUTSTANDING_COMMANDS) { /* Command not found. */ return QLA_FUNCTION_FAILED; @@ -2332,36 +2652,41 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp) abt = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma); if (abt == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Abort IOCB.\n", - __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x108d, + "Failed to allocate abort IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(abt, 0, sizeof(struct abort_entry_24xx)); abt->entry_type = ABORT_IOCB_TYPE; abt->entry_count = 1; + abt->handle = MAKE_HANDLE(req->id, abt->handle); abt->nport_handle = cpu_to_le16(fcport->loop_id); - abt->handle_to_abort = handle; + abt->handle_to_abort = MAKE_HANDLE(req->id, handle); abt->port_id[0] = fcport->d_id.b.al_pa; abt->port_id[1] = fcport->d_id.b.area; abt->port_id[2] = fcport->d_id.b.domain; - abt->vp_index = fcport->vp_idx; - rval = qla2x00_issue_iocb(ha, abt, abt_dma, 0); + abt->vp_index = fcport->vha->vp_idx; + + abt->req_que_no = cpu_to_le16(req->id); + + rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n", - __func__, ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x108e, + "Failed to issue IOCB (%x).\n", rval); } else if (abt->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, ha->host_no, - abt->entry_status)); + ql_dbg(ql_dbg_mbx, vha, 0x108f, + "Failed to complete IOCB -- error status (%x).\n", + abt->entry_status); rval = QLA_FUNCTION_FAILED; } else if (abt->nport_handle != __constant_cpu_to_le16(0)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, ha->host_no, - le16_to_cpu(abt->nport_handle))); + ql_dbg(ql_dbg_mbx, vha, 0x1090, + "Failed to complete IOCB -- completion status (%x).\n", + le16_to_cpu(abt->nport_handle)); rval = QLA_FUNCTION_FAILED; } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091, + "Done %s.\n", __func__); } dma_pool_free(ha->s_dma_pool, abt, abt_dma); @@ -2378,108 +2703,147 @@ struct tsk_mgmt_cmd { static int __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, - unsigned int l) + unsigned int l, int tag) { int rval, rval2; struct tsk_mgmt_cmd *tsk; + struct sts_entry_24xx *sts; dma_addr_t tsk_dma; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct req_que *req; + struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no)); + vha = fcport->vha; + ha = vha->hw; + req = vha->req; - ha = fcport->ha; - pha = to_qla_parent(ha); - tsk = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &tsk_dma); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1092, + "Entered %s.\n", __func__); + + if (ha->flags.cpu_affinity_enabled) + rsp = ha->rsp_q_map[tag + 1]; + else + rsp = req->rsp; + tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma); if (tsk == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Task Management " - "IOCB.\n", __func__, ha->host_no)); + ql_log(ql_log_warn, vha, 0x1093, + "Failed to allocate task management IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(tsk, 0, sizeof(struct tsk_mgmt_cmd)); tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE; tsk->p.tsk.entry_count = 1; + tsk->p.tsk.handle = MAKE_HANDLE(req->id, tsk->p.tsk.handle); tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id); tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); tsk->p.tsk.control_flags = cpu_to_le32(type); tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa; tsk->p.tsk.port_id[1] = fcport->d_id.b.area; tsk->p.tsk.port_id[2] = fcport->d_id.b.domain; - tsk->p.tsk.vp_index = fcport->vp_idx; + tsk->p.tsk.vp_index = fcport->vha->vp_idx; if (type == TCF_LUN_RESET) { int_to_scsilun(l, &tsk->p.tsk.lun); host_to_fcp_swap((uint8_t *)&tsk->p.tsk.lun, sizeof(tsk->p.tsk.lun)); } - rval = qla2x00_issue_iocb(ha, tsk, tsk_dma, 0); + sts = &tsk->p.sts; + rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB " - "(%x).\n", __func__, ha->host_no, name, rval)); - } else if (tsk->p.sts.entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, ha->host_no, - tsk->p.sts.entry_status)); + ql_dbg(ql_dbg_mbx, vha, 0x1094, + "Failed to issue %s reset IOCB (%x).\n", name, rval); + } else if (sts->entry_status != 0) { + ql_dbg(ql_dbg_mbx, vha, 0x1095, + "Failed to complete IOCB -- error status (%x).\n", + sts->entry_status); rval = QLA_FUNCTION_FAILED; - } else if (tsk->p.sts.comp_status != + } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, - ha->host_no, le16_to_cpu(tsk->p.sts.comp_status))); + ql_dbg(ql_dbg_mbx, vha, 0x1096, + "Failed to complete IOCB -- completion status (%x).\n", + le16_to_cpu(sts->comp_status)); rval = QLA_FUNCTION_FAILED; + } else if (le16_to_cpu(sts->scsi_status) & + SS_RESPONSE_INFO_LEN_VALID) { + if (le32_to_cpu(sts->rsp_data_len) < 4) { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097, + "Ignoring inconsistent data length -- not enough " + "response info (%d).\n", + le32_to_cpu(sts->rsp_data_len)); + } else if (sts->data[3]) { + ql_dbg(ql_dbg_mbx, vha, 0x1098, + "Failed to complete IOCB -- response (%x).\n", + sts->data[3]); + rval = QLA_FUNCTION_FAILED; + } } /* Issue marker IOCB. */ - rval2 = qla2x00_marker(ha, fcport->loop_id, l, + rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l, type == TCF_LUN_RESET ? MK_SYNC_ID_LUN: MK_SYNC_ID); if (rval2 != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB " - "(%x).\n", __func__, ha->host_no, rval2)); + ql_dbg(ql_dbg_mbx, vha, 0x1099, + "Failed to issue marker IOCB (%x).\n", rval2); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109a, + "Done %s.\n", __func__); } - dma_pool_free(pha->s_dma_pool, tsk, tsk_dma); + dma_pool_free(ha->s_dma_pool, tsk, tsk_dma); return rval; } int -qla24xx_abort_target(struct fc_port *fcport, unsigned int l) +qla24xx_abort_target(struct fc_port *fcport, unsigned int l, int tag) { - return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l); + struct qla_hw_data *ha = fcport->vha->hw; + + if ((ql2xasynctmfenable) && IS_FWI2_CAPABLE(ha)) + return qla2x00_async_tm_cmd(fcport, TCF_TARGET_RESET, l, tag); + + return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l, tag); } int -qla24xx_lun_reset(struct fc_port *fcport, unsigned int l) +qla24xx_lun_reset(struct fc_port *fcport, unsigned int l, int tag) { - return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l); + struct qla_hw_data *ha = fcport->vha->hw; + + if ((ql2xasynctmfenable) && IS_FWI2_CAPABLE(ha)) + return qla2x00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag); + + return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l, tag); } int -qla2x00_system_error(scsi_qla_host_t *ha) +qla2x00_system_error(scsi_qla_host_t *vha) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109b, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GEN_SYSTEM_ERROR; mcp->out_mb = MBX_0; mcp->in_mb = MBX_0; mcp->tov = 5; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x109c, "Failed=%x.\n", rval); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109d, + "Done %s.\n", __func__); } return rval; @@ -2492,14 +2856,15 @@ qla2x00_system_error(scsi_qla_host_t *ha) * Returns */ int -qla2x00_set_serdes_params(scsi_qla_host_t *ha, uint16_t sw_em_1g, +qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g, uint16_t sw_em_2g, uint16_t sw_em_4g) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109e, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_SERDES_PARAMS; mcp->mb[1] = BIT_0; @@ -2510,61 +2875,70 @@ qla2x00_set_serdes_params(scsi_qla_host_t *ha, uint16_t sw_em_1g, mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, - ha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x109f, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a0, + "Done %s.\n", __func__); } return rval; } int -qla2x00_stop_firmware(scsi_qla_host_t *ha) +qla2x00_stop_firmware(scsi_qla_host_t *vha) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_FWI2_CAPABLE(ha)) + if (!IS_FWI2_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a1, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_STOP_FIRMWARE; - mcp->out_mb = MBX_0; + mcp->mb[1] = 0; + mcp->out_mb = MBX_1|MBX_0; mcp->in_mb = MBX_0; mcp->tov = 5; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10a2, "Failed=%x.\n", rval); + if (mcp->mb[0] == MBS_INVALID_COMMAND) + rval = QLA_INVALID_COMMAND; } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a3, + "Done %s.\n", __func__); } return rval; } int -qla2x00_enable_eft_trace(scsi_qla_host_t *ha, dma_addr_t eft_dma, +qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma, uint16_t buffers) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_FWI2_CAPABLE(ha)) + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a4, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + if (unlikely(pci_channel_offline(vha->hw->pdev))) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_EFT_ENABLE; @@ -2578,28 +2952,34 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *ha, dma_addr_t eft_dma, mcp->in_mb = MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10a5, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a6, + "Done %s.\n", __func__); } return rval; } int -qla2x00_disable_eft_trace(scsi_qla_host_t *ha) +qla2x00_disable_eft_trace(scsi_qla_host_t *vha) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_FWI2_CAPABLE(ha)) + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a7, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + if (unlikely(pci_channel_offline(vha->hw->pdev))) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_EFT_DISABLE; @@ -2607,29 +2987,36 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *ha) mcp->in_mb = MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10a8, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a9, + "Done %s.\n", __func__); } return rval; } int -qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma, +qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma, uint16_t buffers, uint16_t *mb, uint32_t *dwords) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA25XX(ha)) + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10aa, + "Entered %s.\n", __func__); + + if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) && + !IS_QLA83XX(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + if (unlikely(pci_channel_offline(vha->hw->pdev))) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_FCE_ENABLE; @@ -2647,12 +3034,14 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma, mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10ab, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ac, + "Done %s.\n", __func__); if (mb) memcpy(mb, mcp->mb, 8 * sizeof(*mb)); @@ -2664,16 +3053,20 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma, } int -qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd) +qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_FWI2_CAPABLE(ha)) + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ad, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + if (unlikely(pci_channel_offline(vha->hw->pdev))) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_FCE_DISABLE; @@ -2683,12 +3076,14 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd) MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10ae, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10af, + "Done %s.\n", __func__); if (wr) *wr = (uint64_t) mcp->mb[5] << 48 | @@ -2706,66 +3101,76 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd) } int -qla2x00_read_sfp(scsi_qla_host_t *ha, dma_addr_t sfp_dma, uint16_t addr, - uint16_t off, uint16_t count) +qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, + uint16_t *port_speed, uint16_t *mb) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_FWI2_CAPABLE(ha)) + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b0, + "Entered %s.\n", __func__); + + if (!IS_IIDMA_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); - - mcp->mb[0] = MBC_READ_SFP; - mcp->mb[1] = addr; - mcp->mb[2] = MSW(sfp_dma); - mcp->mb[3] = LSW(sfp_dma); - mcp->mb[6] = MSW(MSD(sfp_dma)); - mcp->mb[7] = LSW(MSD(sfp_dma)); - mcp->mb[8] = count; - mcp->mb[9] = off; - mcp->mb[10] = 0; - mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; - mcp->in_mb = MBX_0; + mcp->mb[0] = MBC_PORT_PARAMS; + mcp->mb[1] = loop_id; + mcp->mb[2] = mcp->mb[3] = 0; + mcp->mb[9] = vha->vp_idx; + mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); + + /* Return mailbox statuses. */ + if (mb != NULL) { + mb[0] = mcp->mb[0]; + mb[1] = mcp->mb[1]; + mb[3] = mcp->mb[3]; + } if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, - ha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x10b1, "Failed=%x.\n", rval); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b2, + "Done %s.\n", __func__); + if (port_speed) + *port_speed = mcp->mb[3]; } return rval; } int -qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, +qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t port_speed, uint16_t *mb) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_IIDMA_CAPABLE(ha)) - return QLA_FUNCTION_FAILED; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b3, + "Entered %s.\n", __func__); - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + if (!IS_IIDMA_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_PORT_PARAMS; mcp->mb[1] = loop_id; mcp->mb[2] = BIT_0; - mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0); + if (IS_CNA_CAPABLE(vha->hw)) + mcp->mb[3] = port_speed & (BIT_5|BIT_4|BIT_3|BIT_2|BIT_1|BIT_0); + else + mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0); mcp->mb[4] = mcp->mb[5] = 0; - mcp->out_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; - mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_1|MBX_0; + mcp->mb[9] = vha->vp_idx; + mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); /* Return mailbox statuses. */ if (mb != NULL) { @@ -2777,71 +3182,89 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, } if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10b4, + "Failed=%x.\n", rval); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b5, + "Done %s.\n", __func__); } return rval; } void -qla24xx_report_id_acquisition(scsi_qla_host_t *ha, +qla24xx_report_id_acquisition(scsi_qla_host_t *vha, struct vp_rpt_id_entry_24xx *rptid_entry) { uint8_t vp_idx; uint16_t stat = le16_to_cpu(rptid_entry->vp_idx); - scsi_qla_host_t *vha; + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *vp; + unsigned long flags; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6, + "Entered %s.\n", __func__); if (rptid_entry->entry_status != 0) return; if (rptid_entry->format == 0) { - DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d," - " number of VPs acquired %d\n", __func__, ha->host_no, - MSB(rptid_entry->vp_count), LSB(rptid_entry->vp_count))); - DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__, - rptid_entry->port_id[2], rptid_entry->port_id[1], - rptid_entry->port_id[0])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b7, + "Format 0 : Number of VPs setup %d, number of " + "VPs acquired %d.\n", + MSB(le16_to_cpu(rptid_entry->vp_count)), + LSB(le16_to_cpu(rptid_entry->vp_count))); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b8, + "Primary port id %02x%02x%02x.\n", + rptid_entry->port_id[2], rptid_entry->port_id[1], + rptid_entry->port_id[0]); } else if (rptid_entry->format == 1) { bool found; vp_idx = LSB(stat); - DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled " - "- status %d - " - "with port id %02x%02x%02x\n",__func__,ha->host_no, - vp_idx, MSB(stat), + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b9, + "Format 1: VP[%d] enabled - status %d - with " + "port id %02x%02x%02x.\n", vp_idx, MSB(stat), rptid_entry->port_id[2], rptid_entry->port_id[1], - rptid_entry->port_id[0])); - if (vp_idx == 0) - return; + rptid_entry->port_id[0]); - if (MSB(stat) == 1) + vp = vha; + if (vp_idx == 0 && (MSB(stat) != 1)) + goto reg_needed; + + if (MSB(stat) != 0) { + ql_dbg(ql_dbg_mbx, vha, 0x10ba, + "Could not acquire ID for VP[%d].\n", vp_idx); return; + } found = false; - list_for_each_entry(vha, &ha->vp_list, vp_list) - if (vp_idx == vha->vp_idx) { + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) + if (vp_idx == vp->vp_idx) { found = true; break; } + spin_unlock_irqrestore(&ha->vport_slock, flags); if (!found) return; - vha->d_id.b.domain = rptid_entry->port_id[2]; - vha->d_id.b.area = rptid_entry->port_id[1]; - vha->d_id.b.al_pa = rptid_entry->port_id[0]; + vp->d_id.b.domain = rptid_entry->port_id[2]; + vp->d_id.b.area = rptid_entry->port_id[1]; + vp->d_id.b.al_pa = rptid_entry->port_id[0]; /* * Cannot configure here as we are still sitting on the * response queue. Handle it in dpc context. */ - set_bit(VP_IDX_ACQUIRED, &vha->vp_flags); - set_bit(VP_DPC_NEEDED, &ha->dpc_flags); + set_bit(VP_IDX_ACQUIRED, &vp->vp_flags); - qla2xxx_wake_dpc(ha); +reg_needed: + set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags); + set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags); + set_bit(VP_DPC_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); } } @@ -2864,15 +3287,23 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) int rval; struct vp_config_entry_24xx *vpmod; dma_addr_t vpmod_dma; - scsi_qla_host_t *pha; + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + + if (!qla_firmware_active(vha)) { + rval = QLA_SUCCESS; + goto out; + } /* This can be called by the parent */ - pha = to_qla_parent(vha); - vpmod = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &vpmod_dma); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10bb, + "Entered %s.\n", __func__); + + vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma); if (!vpmod) { - DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP " - "IOCB.\n", __func__, pha->host_no)); + ql_log(ql_log_warn, vha, 0x10bc, + "Failed to allocate modify VP IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } @@ -2895,27 +3326,29 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE); vpmod->entry_count = 1; - rval = qla2x00_issue_iocb(pha, vpmod, vpmod_dma, 0); + rval = qla2x00_issue_iocb(base_vha, vpmod, vpmod_dma, 0); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB" - "(%x).\n", __func__, pha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10bd, + "Failed to issue VP config IOCB (%x).\n", rval); } else if (vpmod->comp_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, pha->host_no, - vpmod->comp_status)); + ql_dbg(ql_dbg_mbx, vha, 0x10be, + "Failed to complete IOCB -- error status (%x).\n", + vpmod->comp_status); rval = QLA_FUNCTION_FAILED; } else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, pha->host_no, - le16_to_cpu(vpmod->comp_status))); + ql_dbg(ql_dbg_mbx, vha, 0x10bf, + "Failed to complete IOCB -- completion status (%x).\n", + le16_to_cpu(vpmod->comp_status)); rval = QLA_FUNCTION_FAILED; } else { /* EMPTY */ - DEBUG11(printk("%s(%ld): done.\n", __func__, pha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0, + "Done %s.\n", __func__); fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING); } - dma_pool_free(pha->s_dma_pool, vpmod, vpmod_dma); + dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma); +out: return rval; } @@ -2941,20 +3374,25 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) int map, pos; struct vp_ctrl_entry_24xx *vce; dma_addr_t vce_dma; - scsi_qla_host_t *ha = vha->parent; + struct qla_hw_data *ha = vha->hw; int vp_index = vha->vp_idx; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__, - ha->host_no, vp_index)); + if (!qla_firmware_active(vha)) { + rval = QLA_SUCCESS; + goto out; + } + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c1, + "Entered %s enabling index %d.\n", __func__, vp_index); if (vp_index == 0 || vp_index >= ha->max_npiv_vports) return QLA_PARAMETER_ERROR; vce = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vce_dma); if (!vce) { - DEBUG2_3(printk("%s(%ld): " - "failed to allocate VP Control IOCB.\n", __func__, - ha->host_no)); + ql_log(ql_log_warn, vha, 0x10c2, + "Failed to allocate VP control IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx)); @@ -2973,34 +3411,33 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) vce->vp_idx_map[map] |= 1 << pos; mutex_unlock(&ha->vport_lock); - rval = qla2x00_issue_iocb(ha, vce, vce_dma, 0); + rval = qla2x00_issue_iocb(base_vha, vce, vce_dma, 0); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB" - "(%x).\n", __func__, ha->host_no, rval)); - printk("%s(%ld): failed to issue VP control IOCB" - "(%x).\n", __func__, ha->host_no, rval); + ql_dbg(ql_dbg_mbx, vha, 0x10c3, + "Failed to issue VP control IOCB (%x).\n", rval); } else if (vce->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, ha->host_no, - vce->entry_status)); - printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, ha->host_no, + ql_dbg(ql_dbg_mbx, vha, 0x10c4, + "Failed to complete IOCB -- error status (%x).\n", vce->entry_status); rval = QLA_FUNCTION_FAILED; } else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, ha->host_no, - le16_to_cpu(vce->comp_status))); - printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, ha->host_no, + /* + * It can fail, because this function can be called 2 times on + * VPs delete: one from q2t_target_stop() and another - from + * qla24xx_vport_delete(). The second call will fail. + */ + ql_dbg(ql_dbg_mbx, vha, 0x10c5, + "Failed to complet IOCB -- completion status (%x).\n", le16_to_cpu(vce->comp_status)); rval = QLA_FUNCTION_FAILED; } else { - DEBUG2(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c6, + "Done %s.\n", __func__); } dma_pool_free(ha->s_dma_pool, vce, vce_dma); +out: return rval; } @@ -3026,18 +3463,21 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) */ int -qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format, +qla2x00_send_change_request(scsi_qla_host_t *vha, uint16_t format, uint16_t vp_idx) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c7, + "Entered %s.\n", __func__); + /* * This command is implicitly executed by firmware during login for the * physical hosts if initiator mode is enabled. */ - if (vp_idx == 0 && (ha->host->active_mode & MODE_INITIATOR)) + if (vp_idx == 0 && (vha->host->active_mode & MODE_INITIATOR)) return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_SEND_CHANGE_REQUEST; @@ -3047,7 +3487,7 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format, mcp->in_mb = MBX_0|MBX_1; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval == QLA_SUCCESS) { if (mcp->mb[0] != MBS_COMMAND_COMPLETE) { @@ -3061,16 +3501,17 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format, EXPORT_SYMBOL(qla2x00_send_change_request); int -qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr, +qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr, uint32_t size) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1009, + "Entered %s.\n", __func__); - if (MSW(addr) || IS_FWI2_CAPABLE(ha)) { + if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED; mcp->mb[8] = MSW(addr); mcp->out_mb = MBX_8|MBX_0; @@ -3084,7 +3525,7 @@ qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr, mcp->mb[6] = MSW(MSD(req_dma)); mcp->mb[7] = LSW(MSD(req_dma)); mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1; - if (IS_FWI2_CAPABLE(ha)) { + if (IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[4] = MSW(size); mcp->mb[5] = LSW(size); mcp->out_mb |= MBX_5|MBX_4; @@ -3096,13 +3537,14 @@ qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr, mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - ha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1008, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1007, + "Done %s.\n", __func__); } return rval; @@ -3118,20 +3560,20 @@ struct cs84xx_mgmt_cmd { }; int -qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status) +qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status) { int rval, retry; struct cs84xx_mgmt_cmd *mn; dma_addr_t mn_dma; uint16_t options; unsigned long flags; + struct qla_hw_data *ha = vha->hw; - DEBUG16(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c8, + "Entered %s.\n", __func__); mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); if (mn == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Verify ISP84XX " - "IOCB.\n", __func__, ha->host_no)); return QLA_MEMORY_ALLOC_FAILED; } @@ -3149,43 +3591,43 @@ qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status) mn->p.req.entry_count = 1; mn->p.req.options = cpu_to_le16(options); - DEBUG16(printk("%s(%ld): Dump of Verify Request.\n", __func__, - ha->host_no)); - DEBUG16(qla2x00_dump_buffer((uint8_t *)mn, - sizeof(*mn))); + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111c, + "Dump of Verify Request.\n"); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111e, + (uint8_t *)mn, sizeof(*mn)); - rval = qla2x00_issue_iocb_timeout(ha, mn, mn_dma, 0, 120); + rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120); if (rval != QLA_SUCCESS) { - DEBUG2_16(printk("%s(%ld): failed to issue Verify " - "IOCB (%x).\n", __func__, ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10cb, + "Failed to issue verify IOCB (%x).\n", rval); goto verify_done; } - DEBUG16(printk("%s(%ld): Dump of Verify Response.\n", __func__, - ha->host_no)); - DEBUG16(qla2x00_dump_buffer((uint8_t *)mn, - sizeof(*mn))); + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1110, + "Dump of Verify Response.\n"); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1118, + (uint8_t *)mn, sizeof(*mn)); status[0] = le16_to_cpu(mn->p.rsp.comp_status); status[1] = status[0] == CS_VCS_CHIP_FAILURE ? le16_to_cpu(mn->p.rsp.failure_code) : 0; - DEBUG2_16(printk("%s(%ld): cs=%x fc=%x\n", __func__, - ha->host_no, status[0], status[1])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ce, + "cs=%x fc=%x.\n", status[0], status[1]); if (status[0] != CS_COMPLETE) { rval = QLA_FUNCTION_FAILED; if (!(options & VCO_DONT_UPDATE_FW)) { - DEBUG2_16(printk("%s(%ld): Firmware update " - "failed. Retrying without update " - "firmware.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx, vha, 0x10cf, + "Firmware update failed. Retrying " + "without update firmware.\n"); options |= VCO_DONT_UPDATE_FW; options &= ~VCO_FORCE_UPDATE; retry = 1; } } else { - DEBUG2_16(printk("%s(%ld): firmware updated to %x.\n", - __func__, ha->host_no, - le32_to_cpu(mn->p.rsp.fw_ver))); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d0, + "Firmware updated to %x.\n", + le32_to_cpu(mn->p.rsp.fw_ver)); /* NOTE: we only update OP firmware. */ spin_lock_irqsave(&ha->cs84xx->access_lock, flags); @@ -3200,11 +3642,1390 @@ verify_done: dma_pool_free(ha->s_dma_pool, mn, mn_dma); if (rval != QLA_SUCCESS) { - DEBUG2_16(printk("%s(%ld): failed=%x.\n", __func__, - ha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10d1, + "Failed=%x.\n", rval); } else { - DEBUG16(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d2, + "Done %s.\n", __func__); } return rval; } + +int +qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) +{ + int rval; + unsigned long flags; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct device_reg_25xxmq __iomem *reg; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d3, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_INITIALIZE_MULTIQ; + mcp->mb[1] = req->options; + mcp->mb[2] = MSW(LSD(req->dma)); + mcp->mb[3] = LSW(LSD(req->dma)); + mcp->mb[6] = MSW(MSD(req->dma)); + mcp->mb[7] = LSW(MSD(req->dma)); + mcp->mb[5] = req->length; + if (req->rsp) + mcp->mb[10] = req->rsp->id; + mcp->mb[12] = req->qos; + mcp->mb[11] = req->vp_idx; + mcp->mb[13] = req->rid; + if (IS_QLA83XX(ha)) + mcp->mb[15] = 0; + + reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) + + QLA_QUE_PAGE * req->id); + + mcp->mb[4] = req->id; + /* que in ptr index */ + mcp->mb[8] = 0; + /* que out ptr index */ + mcp->mb[9] = 0; + mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7| + MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->flags = MBX_DMA_OUT; + mcp->tov = MBX_TOV_SECONDS * 2; + + if (IS_QLA81XX(ha) || IS_QLA83XX(ha)) + mcp->in_mb |= MBX_1; + if (IS_QLA83XX(ha)) { + mcp->out_mb |= MBX_15; + /* debug q create issue in SR-IOV */ + mcp->in_mb |= MBX_9 | MBX_8 | MBX_7; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (!(req->options & BIT_0)) { + WRT_REG_DWORD(®->req_q_in, 0); + if (!IS_QLA83XX(ha)) + WRT_REG_DWORD(®->req_q_out, 0); + } + req->req_q_in = ®->req_q_in; + req->req_q_out = ®->req_q_out; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10d4, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d5, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) +{ + int rval; + unsigned long flags; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct device_reg_25xxmq __iomem *reg; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d6, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_INITIALIZE_MULTIQ; + mcp->mb[1] = rsp->options; + mcp->mb[2] = MSW(LSD(rsp->dma)); + mcp->mb[3] = LSW(LSD(rsp->dma)); + mcp->mb[6] = MSW(MSD(rsp->dma)); + mcp->mb[7] = LSW(MSD(rsp->dma)); + mcp->mb[5] = rsp->length; + mcp->mb[14] = rsp->msix->entry; + mcp->mb[13] = rsp->rid; + if (IS_QLA83XX(ha)) + mcp->mb[15] = 0; + + reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) + + QLA_QUE_PAGE * rsp->id); + + mcp->mb[4] = rsp->id; + /* que in ptr index */ + mcp->mb[8] = 0; + /* que out ptr index */ + mcp->mb[9] = 0; + mcp->out_mb = MBX_14|MBX_13|MBX_9|MBX_8|MBX_7 + |MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->flags = MBX_DMA_OUT; + mcp->tov = MBX_TOV_SECONDS * 2; + + if (IS_QLA81XX(ha)) { + mcp->out_mb |= MBX_12|MBX_11|MBX_10; + mcp->in_mb |= MBX_1; + } else if (IS_QLA83XX(ha)) { + mcp->out_mb |= MBX_15|MBX_12|MBX_11|MBX_10; + mcp->in_mb |= MBX_1; + /* debug q create issue in SR-IOV */ + mcp->in_mb |= MBX_9 | MBX_8 | MBX_7; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (!(rsp->options & BIT_0)) { + WRT_REG_DWORD(®->rsp_q_out, 0); + if (!IS_QLA83XX(ha)) + WRT_REG_DWORD(®->rsp_q_in, 0); + } + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10d7, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d8, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d9, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_IDC_ACK; + memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); + mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10da, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10db, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10dc, + "Entered %s.\n", __func__); + + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_FLASH_ACCESS_CTRL; + mcp->mb[1] = FAC_OPT_CMD_GET_SECTOR_SIZE; + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10dd, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10de, + "Done %s.\n", __func__); + *sector_size = mcp->mb[1]; + } + + return rval; +} + +int +qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10df, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_FLASH_ACCESS_CTRL; + mcp->mb[1] = enable ? FAC_OPT_CMD_WRITE_ENABLE : + FAC_OPT_CMD_WRITE_PROTECT; + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10e0, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e1, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e2, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_FLASH_ACCESS_CTRL; + mcp->mb[1] = FAC_OPT_CMD_ERASE_SECTOR; + mcp->mb[2] = LSW(start); + mcp->mb[3] = MSW(start); + mcp->mb[4] = LSW(finish); + mcp->mb[5] = MSW(finish); + mcp->out_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_2|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10e3, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e4, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha) +{ + int rval = 0; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e5, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_RESTART_MPI_FW; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_0|MBX_1; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10e6, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e7, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp, + uint16_t dev, uint16_t off, uint16_t len, uint16_t opt) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e8, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(ha)) + return QLA_FUNCTION_FAILED; + + if (len == 1) + opt |= BIT_0; + + mcp->mb[0] = MBC_READ_SFP; + mcp->mb[1] = dev; + mcp->mb[2] = MSW(sfp_dma); + mcp->mb[3] = LSW(sfp_dma); + mcp->mb[6] = MSW(MSD(sfp_dma)); + mcp->mb[7] = LSW(MSD(sfp_dma)); + mcp->mb[8] = len; + mcp->mb[9] = off; + mcp->mb[10] = opt; + mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (opt & BIT_0) + *sfp = mcp->mb[1]; + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10e9, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ea, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp, + uint16_t dev, uint16_t off, uint16_t len, uint16_t opt) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10eb, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(ha)) + return QLA_FUNCTION_FAILED; + + if (len == 1) + opt |= BIT_0; + + if (opt & BIT_0) + len = *sfp; + + mcp->mb[0] = MBC_WRITE_SFP; + mcp->mb[1] = dev; + mcp->mb[2] = MSW(sfp_dma); + mcp->mb[3] = LSW(sfp_dma); + mcp->mb[6] = MSW(MSD(sfp_dma)); + mcp->mb[7] = LSW(MSD(sfp_dma)); + mcp->mb[8] = len; + mcp->mb[9] = off; + mcp->mb[10] = opt; + mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10ec, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ed, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma, + uint16_t size_in_bytes, uint16_t *actual_size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ee, + "Entered %s.\n", __func__); + + if (!IS_CNA_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_GET_XGMAC_STATS; + mcp->mb[2] = MSW(stats_dma); + mcp->mb[3] = LSW(stats_dma); + mcp->mb[6] = MSW(MSD(stats_dma)); + mcp->mb[7] = LSW(MSD(stats_dma)); + mcp->mb[8] = size_in_bytes >> 2; + mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; + mcp->in_mb = MBX_2|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10ef, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f0, + "Done %s.\n", __func__); + + + *actual_size = mcp->mb[2] << 2; + } + + return rval; +} + +int +qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma, + uint16_t size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f1, + "Entered %s.\n", __func__); + + if (!IS_CNA_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_GET_DCBX_PARAMS; + mcp->mb[1] = 0; + mcp->mb[2] = MSW(tlv_dma); + mcp->mb[3] = LSW(tlv_dma); + mcp->mb[6] = MSW(MSD(tlv_dma)); + mcp->mb[7] = LSW(MSD(tlv_dma)); + mcp->mb[8] = size; + mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_2|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10f2, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f3, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f4, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_READ_RAM_EXTENDED; + mcp->mb[1] = LSW(risc_addr); + mcp->mb[8] = MSW(risc_addr); + mcp->out_mb = MBX_8|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_2|MBX_0; + mcp->tov = 30; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10f5, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f6, + "Done %s.\n", __func__); + *data = mcp->mb[3] << 16 | mcp->mb[2]; + } + + return rval; +} + +int +qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mresp) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + uint32_t iter_cnt = 0x1; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f7, + "Entered %s.\n", __func__); + + memset(mcp->mb, 0 , sizeof(mcp->mb)); + mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK; + mcp->mb[1] = mreq->options | BIT_6; // BIT_6 specifies 64 bit addressing + + /* transfer count */ + mcp->mb[10] = LSW(mreq->transfer_size); + mcp->mb[11] = MSW(mreq->transfer_size); + + /* send data address */ + mcp->mb[14] = LSW(mreq->send_dma); + mcp->mb[15] = MSW(mreq->send_dma); + mcp->mb[20] = LSW(MSD(mreq->send_dma)); + mcp->mb[21] = MSW(MSD(mreq->send_dma)); + + /* receive data address */ + mcp->mb[16] = LSW(mreq->rcv_dma); + mcp->mb[17] = MSW(mreq->rcv_dma); + mcp->mb[6] = LSW(MSD(mreq->rcv_dma)); + mcp->mb[7] = MSW(MSD(mreq->rcv_dma)); + + /* Iteration count */ + mcp->mb[18] = LSW(iter_cnt); + mcp->mb[19] = MSW(iter_cnt); + + mcp->out_mb = MBX_21|MBX_20|MBX_19|MBX_18|MBX_17|MBX_16|MBX_15| + MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0; + if (IS_CNA_CAPABLE(vha->hw)) + mcp->out_mb |= MBX_2; + mcp->in_mb = MBX_19|MBX_18|MBX_3|MBX_2|MBX_1|MBX_0; + + mcp->buf_size = mreq->transfer_size; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD; + + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10f8, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[3]=%x mb[18]=%x " + "mb[19]=%x.\n", rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], + mcp->mb[3], mcp->mb[18], mcp->mb[19]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f9, + "Done %s.\n", __func__); + } + + /* Copy mailbox information */ + memcpy(mresp, mcp->mb, 64); + return rval; +} + +int +qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mresp) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fa, + "Entered %s.\n", __func__); + + memset(mcp->mb, 0 , sizeof(mcp->mb)); + mcp->mb[0] = MBC_DIAGNOSTIC_ECHO; + mcp->mb[1] = mreq->options | BIT_6; /* BIT_6 specifies 64bit address */ + if (IS_CNA_CAPABLE(ha)) { + mcp->mb[1] |= BIT_15; + mcp->mb[2] = vha->fcoe_fcf_idx; + } + mcp->mb[16] = LSW(mreq->rcv_dma); + mcp->mb[17] = MSW(mreq->rcv_dma); + mcp->mb[6] = LSW(MSD(mreq->rcv_dma)); + mcp->mb[7] = MSW(MSD(mreq->rcv_dma)); + + mcp->mb[10] = LSW(mreq->transfer_size); + + mcp->mb[14] = LSW(mreq->send_dma); + mcp->mb[15] = MSW(mreq->send_dma); + mcp->mb[20] = LSW(MSD(mreq->send_dma)); + mcp->mb[21] = MSW(MSD(mreq->send_dma)); + + mcp->out_mb = MBX_21|MBX_20|MBX_17|MBX_16|MBX_15| + MBX_14|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0; + if (IS_CNA_CAPABLE(ha)) + mcp->out_mb |= MBX_2; + + mcp->in_mb = MBX_0; + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || + IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) + mcp->in_mb |= MBX_1; + if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) + mcp->in_mb |= MBX_3; + + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD; + mcp->buf_size = mreq->transfer_size; + + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10fb, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fc, + "Done %s.\n", __func__); + } + + /* Copy mailbox information */ + memcpy( mresp, mcp->mb, 64); + return rval; +} + +int +qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fd, + "Entered %s enable_diag=%d.\n", __func__, enable_diagnostic); + + mcp->mb[0] = MBC_ISP84XX_RESET; + mcp->mb[1] = enable_diagnostic; + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) + ql_dbg(ql_dbg_mbx, vha, 0x10fe, "Failed=%x.\n", rval); + else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ff, + "Done %s.\n", __func__); + + return rval; +} + +int +qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1100, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_WRITE_RAM_WORD_EXTENDED; + mcp->mb[1] = LSW(risc_addr); + mcp->mb[2] = LSW(data); + mcp->mb[3] = MSW(data); + mcp->mb[8] = MSW(risc_addr); + mcp->out_mb = MBX_8|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = 30; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1101, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1102, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb) +{ + int rval; + uint32_t stat, timer; + uint16_t mb0 = 0; + struct qla_hw_data *ha = vha->hw; + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + + rval = QLA_SUCCESS; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1103, + "Entered %s.\n", __func__); + + clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + + /* Write the MBC data to the registers */ + WRT_REG_WORD(®->mailbox0, MBC_WRITE_MPI_REGISTER); + WRT_REG_WORD(®->mailbox1, mb[0]); + WRT_REG_WORD(®->mailbox2, mb[1]); + WRT_REG_WORD(®->mailbox3, mb[2]); + WRT_REG_WORD(®->mailbox4, mb[3]); + + WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT); + + /* Poll for MBC interrupt */ + for (timer = 6000000; timer; timer--) { + /* Check for pending interrupts. */ + stat = RD_REG_DWORD(®->host_status); + if (stat & HSRX_RISC_INT) { + stat &= 0xff; + + if (stat == 0x1 || stat == 0x2 || + stat == 0x10 || stat == 0x11) { + set_bit(MBX_INTERRUPT, + &ha->mbx_cmd_flags); + mb0 = RD_REG_WORD(®->mailbox0); + WRT_REG_DWORD(®->hccr, + HCCRX_CLR_RISC_INT); + RD_REG_DWORD(®->hccr); + break; + } + } + udelay(5); + } + + if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) + rval = mb0 & MBS_MASK; + else + rval = QLA_FUNCTION_FAILED; + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1104, + "Failed=%x mb[0]=%x.\n", rval, mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1105, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_get_data_rate(scsi_qla_host_t *vha) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1106, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(ha)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_DATA_RATE; + mcp->mb[1] = 0; + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_2|MBX_1|MBX_0; + if (IS_QLA83XX(ha)) + mcp->in_mb |= MBX_3; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1107, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108, + "Done %s.\n", __func__); + if (mcp->mb[1] != 0x7) + ha->link_data_rate = mcp->mb[1]; + } + + return rval; +} + +int +qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1109, + "Entered %s.\n", __func__); + + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + mcp->mb[0] = MBC_GET_PORT_CONFIG; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x110a, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + /* Copy all bits to preserve original value */ + memcpy(mb, &mcp->mb[1], sizeof(uint16_t) * 4); + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b, + "Done %s.\n", __func__); + } + return rval; +} + +int +qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110c, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_SET_PORT_CONFIG; + /* Copy all bits to preserve original setting */ + memcpy(&mcp->mb[1], mb, sizeof(uint16_t) * 4); + mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x110d, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110e, + "Done %s.\n", __func__); + + return rval; +} + + +int +qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority, + uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110f, + "Entered %s.\n", __func__); + + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_PORT_PARAMS; + mcp->mb[1] = loop_id; + if (ha->flags.fcp_prio_enabled) + mcp->mb[2] = BIT_1; + else + mcp->mb[2] = BIT_2; + mcp->mb[4] = priority & 0xf; + mcp->mb[9] = vha->vp_idx; + mcp->out_mb = MBX_9|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_4|MBX_3|MBX_1|MBX_0; + mcp->tov = 30; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + if (mb != NULL) { + mb[0] = mcp->mb[0]; + mb[1] = mcp->mb[1]; + mb[3] = mcp->mb[3]; + mb[4] = mcp->mb[4]; + } + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10cd, "Failed=%x.\n", rval); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10cc, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac) +{ + int rval; + uint8_t byte; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ca, + "Entered %s.\n", __func__); + + /* Integer part */ + rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x10c9, "Failed=%x.\n", rval); + ha->flags.thermal_supported = 0; + goto fail; + } + *temp = byte; + + /* Fraction part */ + rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x10, 1, BIT_13|BIT_0); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1019, "Failed=%x.\n", rval); + ha->flags.thermal_supported = 0; + goto fail; + } + *frac = (byte >> 6) * 25; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1018, + "Done %s.\n", __func__); +fail: + return rval; +} + +int +qla82xx_mbx_intr_enable(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1017, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(ha)) + return QLA_FUNCTION_FAILED; + + memset(mcp, 0, sizeof(mbx_cmd_t)); + mcp->mb[0] = MBC_TOGGLE_INTERRUPT; + mcp->mb[1] = 1; + + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = 30; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1016, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100e, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla82xx_mbx_intr_disable(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100d, + "Entered %s.\n", __func__); + + if (!IS_QLA82XX(ha)) + return QLA_FUNCTION_FAILED; + + memset(mcp, 0, sizeof(mbx_cmd_t)); + mcp->mb[0] = MBC_TOGGLE_INTERRUPT; + mcp->mb[1] = 0; + + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = 30; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x100c, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100b, + "Done %s.\n", __func__); + } + + return rval; +} +int +qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA82XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1127, + "Entered %s.\n", __func__); + + memset(mcp, 0, sizeof(mbx_cmd_t)); + mcp->mb[0] = MBC_SET_LED_CONFIG; + if (enable) + mcp->mb[7] = 0xE; + else + mcp->mb[7] = 0xD; + + mcp->out_mb = MBX_7|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1128, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1129, + "Done %s.\n", __func__); + } + + return rval; +} + +/* + * Command used establish parameters to enable fw to allocate resources for VFs, and + * is therefore only valid from the PF driver in a "write" mode. The command may also + * be used to determine current configuration parameters after fw has been initialised. + * + * FIXME - come back and set this to used passed in parameter block. + */ +int +qla83xx_configure_vfs(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + uint16_t options; + uint32_t vf_cfgblk_size; + dma_addr_t buf_dma; + uint8_t *buf; + + if (!IS_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113a, + "Entered %s.\n", __func__); + + options = BIT_0; // write enable + vf_cfgblk_size = 64; + buf = dma_alloc_coherent(&ha->pdev->dev, + vf_cfgblk_size, &buf_dma, GFP_KERNEL); + if (buf == NULL) { + ql_log(ql_log_warn, vha, 0x112e, + "%s: can't allocate memory.\n", __func__); + return QLA_FUNCTION_FAILED; + } + + memset(buf, 0, vf_cfgblk_size); + buf[0] = 0x0f; // enable VFs 0-3 as trusted + buf[0x10] = 0; // 256 total VPS + buf[0x11] = 0x01; + buf[0x12] = 0x10; // 16 VPs / function + buf[0x13] = 0; + buf[0x14] = 0x40; // 64 NPortHandles / function + buf[0x15] = 0; + buf[0x16] = 0x10; // 16 queue pairs / function + buf[0x17] = 0; + buf[0x18] = 0x20; // should be 32 byte pages for VF queue pairs (128 system pages) + buf[0x19] = 0; + buf[0x1a] = 0; // 128 system pages + buf[0x1b] = 0x80; // local paging enabled for VF + + mcp->mb[7] = LSW(MSD(buf_dma)); + mcp->mb[6] = MSW(MSD(buf_dma)); + mcp->mb[3] = LSW(buf_dma); + mcp->mb[2] = MSW(buf_dma); + mcp->mb[1] = options; + mcp->mb[0] = MBC_CONFIGURE_VF; + + mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6| + MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x112d, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x112c, + "Done %s.\n", __func__); + + dma_free_coherent(&ha->pdev->dev, vf_cfgblk_size, buf, buf_dma); + return rval; +} + +int +qla82xx_md_get_template_size(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int rval = QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x111f, + "Entered %s.\n", __func__); + + memset(mcp->mb, 0 , sizeof(mcp->mb)); + mcp->mb[0] = LSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE); + mcp->mb[1] = MSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE); + mcp->mb[2] = LSW(RQST_TMPLT_SIZE); + mcp->mb[3] = MSW(RQST_TMPLT_SIZE); + + mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8| \ + MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + + mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD; + mcp->tov = MBX_TOV_SECONDS; + rval = qla2x00_mailbox_command(vha, mcp); + + /* Always copy back return mailbox values. */ + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1120, + "mailbox command FAILED=0x%x, subcode=%x.\n", + (mcp->mb[1] << 16) | mcp->mb[0], + (mcp->mb[3] << 16) | mcp->mb[2]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1121, + "Done %s.\n", __func__); + ha->md_template_size = ((mcp->mb[3] << 16) | mcp->mb[2]); + if (!ha->md_template_size) { + ql_dbg(ql_dbg_mbx, vha, 0x1122, + "Null template size obtained.\n"); + rval = QLA_FUNCTION_FAILED; + } + } + return rval; +} + +int +qla82xx_md_get_template(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int rval = QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1123, + "Entered %s.\n", __func__); + + ha->md_tmplt_hdr = dma_alloc_coherent(&ha->pdev->dev, + ha->md_template_size, &ha->md_tmplt_hdr_dma, GFP_KERNEL); + if (!ha->md_tmplt_hdr) { + ql_log(ql_log_warn, vha, 0x1124, + "Unable to allocate memory for Minidump template.\n"); + return rval; + } + + memset(mcp->mb, 0 , sizeof(mcp->mb)); + mcp->mb[0] = LSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE); + mcp->mb[1] = MSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE); + mcp->mb[2] = LSW(RQST_TMPLT); + mcp->mb[3] = MSW(RQST_TMPLT); + mcp->mb[4] = LSW(LSD(ha->md_tmplt_hdr_dma)); + mcp->mb[5] = MSW(LSD(ha->md_tmplt_hdr_dma)); + mcp->mb[6] = LSW(MSD(ha->md_tmplt_hdr_dma)); + mcp->mb[7] = MSW(MSD(ha->md_tmplt_hdr_dma)); + mcp->mb[8] = LSW(ha->md_template_size); + mcp->mb[9] = MSW(ha->md_template_size); + + mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD; + mcp->tov = MBX_TOV_SECONDS; + mcp->out_mb = MBX_11|MBX_10|MBX_9|MBX_8| \ + MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1125, + "mailbox command FAILED=0x%x, subcode=%x.\n", + ((mcp->mb[1] << 16) | mcp->mb[0]), + ((mcp->mb[3] << 16) | mcp->mb[2])); + } else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1126, + "Done %s.\n", __func__); + return rval; +} + +int +qla81xx_set_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1133, + "Entered %s.\n", __func__); + + memset(mcp, 0, sizeof(mbx_cmd_t)); + mcp->mb[0] = MBC_SET_LED_CONFIG; + mcp->mb[1] = led_cfg[0]; + mcp->mb[2] = led_cfg[1]; + if (IS_QLA8031(ha)) { + mcp->mb[3] = led_cfg[2]; + mcp->mb[4] = led_cfg[3]; + mcp->mb[5] = led_cfg[4]; + mcp->mb[6] = led_cfg[5]; + } + + mcp->out_mb = MBX_2|MBX_1|MBX_0; + if (IS_QLA8031(ha)) { + mcp->out_mb |= MBX_6|MBX_5|MBX_4|MBX_3; + } + mcp->in_mb = MBX_0; + mcp->tov = 30; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1134, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1135, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla81xx_get_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1136, + "Entered %s.\n", __func__); + + memset(mcp, 0, sizeof(mbx_cmd_t)); + mcp->mb[0] = MBC_GET_LED_CONFIG; + + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_2|MBX_1|MBX_0; + if (IS_QLA8031(ha)) { + mcp->in_mb |= MBX_6|MBX_5|MBX_4|MBX_3; + } + mcp->tov = 30; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1137, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + led_cfg[0] = mcp->mb[1]; + led_cfg[1] = mcp->mb[2]; + if (IS_QLA8031(ha)) { + led_cfg[2] = mcp->mb[3]; + led_cfg[3] = mcp->mb[4]; + led_cfg[4] = mcp->mb[5]; + led_cfg[5] = mcp->mb[6]; + } + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1138, "Done %s\n", __func__); + } + + return rval; +} + +int +qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1130, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_WRITE_REMOTE_REG; + mcp->mb[1] = LSW(reg); + mcp->mb[2] = MSW(reg); + mcp->mb[3] = LSW(data); + mcp->mb[4] = MSW(data); + mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1131, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1132, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (IS_QLA2100(ha) || IS_QLA2200(ha)) { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113b, + "Implicit LOGO Unsupported.\n"); + return QLA_FUNCTION_FAILED; + } + + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113c, + "Entering %s.\n", __func__); + + /* Perform Implicit LOGO. */ + mcp->mb[0] = MBC_PORT_LOGOUT; + mcp->mb[1] = fcport->loop_id; + mcp->mb[10] = BIT_15; + mcp->out_mb = MBX_10|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) + ql_dbg(ql_dbg_mbx, vha, 0x113d, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113e, + "Done %s.\n", __func__); + + return rval; +} + diff --git a/qla2x00t/qla_mid.c b/qla2x00t/qla_mid.c index 34d6e2e2f..29f354856 100644 --- a/qla2x00t/qla_mid.c +++ b/qla2x00t/qla_mid.c @@ -1,12 +1,12 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla_gbl.h" -#include #include #include #include @@ -15,69 +15,104 @@ #include #include +#ifdef CONFIG_SCSI_QLA2XXX_TARGET #include "qla2x_tgt.h" +#endif void qla2x00_vp_stop_timer(scsi_qla_host_t *vha) { - if (vha->parent && vha->timer_active) { + if (vha->vp_idx && vha->timer_active) { del_timer_sync(&vha->timer); vha->timer_active = 0; } } -static void +static uint32_t qla24xx_allocate_vp_id(scsi_qla_host_t *vha) { uint32_t vp_id; - scsi_qla_host_t *ha = vha->parent; + struct qla_hw_data *ha = vha->hw; + unsigned long flags; /* Find an empty slot and assign an vp_id */ mutex_lock(&ha->vport_lock); vp_id = find_first_zero_bit(ha->vp_idx_map, ha->max_npiv_vports + 1); if (vp_id > ha->max_npiv_vports) { - DEBUG15(printk ("vp_id %d is bigger than max-supported %d.\n", - vp_id, ha->max_npiv_vports)); - mutex_unlock(&ha->vport_lock); - } else { - set_bit(vp_id, ha->vp_idx_map); - ha->num_vhosts++; - vha->vp_idx = vp_id; - list_add_tail(&vha->vp_list, &ha->vp_list); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - ha->tgt_vp_map[vp_id].vha = vha; -#endif + ql_dbg(ql_dbg_vport, vha, 0xa000, + "vp_id %d is bigger than max-supported %d.\n", + vp_id, ha->max_npiv_vports); mutex_unlock(&ha->vport_lock); + return vp_id; } + + set_bit(vp_id, ha->vp_idx_map); + ha->num_vhosts++; + vha->vp_idx = vp_id; + + spin_lock_irqsave(&ha->vport_slock, flags); + list_add_tail(&vha->list, &ha->vp_list); + spin_unlock_irqrestore(&ha->vport_slock, flags); + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->tgt_vp_map[vp_id].vha = vha; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + mutex_unlock(&ha->vport_lock); + return vp_id; } void qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) { uint16_t vp_id; - scsi_qla_host_t *ha = vha->parent; + struct qla_hw_data *ha = vha->hw; + unsigned long flags = 0; mutex_lock(&ha->vport_lock); + /* + * Wait for all pending activities to finish before removing vport from + * the list. + * Lock needs to be held for safe removal from the list (it + * ensures no active vp_list traversal while the vport is removed + * from the queue) + */ + spin_lock_irqsave(&ha->vport_slock, flags); + while (atomic_read(&vha->vref_count)) { + spin_unlock_irqrestore(&ha->vport_slock, flags); + + msleep(500); + + spin_lock_irqsave(&ha->vport_slock, flags); + } + list_del(&vha->list); + spin_unlock_irqrestore(&ha->vport_slock, flags); + vp_id = vha->vp_idx; ha->num_vhosts--; clear_bit(vp_id, ha->vp_idx_map); - list_del(&vha->vp_list); + #ifdef CONFIG_SCSI_QLA2XXX_TARGET ha->tgt_vp_map[vp_id].vha = NULL; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ mutex_unlock(&ha->vport_lock); } static scsi_qla_host_t * -qla24xx_find_vhost_by_name(scsi_qla_host_t *ha, uint8_t *port_name) +qla24xx_find_vhost_by_name(struct qla_hw_data *ha, uint8_t *port_name) { scsi_qla_host_t *vha; + struct scsi_qla_host *tvha; + unsigned long flags; + spin_lock_irqsave(&ha->vport_slock, flags); /* Locate matching device in database. */ - list_for_each_entry(vha, &ha->vp_list, vp_list) { - if (!memcmp(port_name, vha->port_name, WWN_SIZE)) + list_for_each_entry_safe(vha, tvha, &ha->vp_list, list) { + if (!memcmp(port_name, vha->port_name, WWN_SIZE)) { + spin_unlock_irqrestore(&ha->vport_slock, flags); return vha; + } } + spin_unlock_irqrestore(&ha->vport_slock, flags); return NULL; } @@ -97,40 +132,39 @@ qla24xx_find_vhost_by_name(scsi_qla_host_t *ha, uint8_t *port_name) static void qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) { + /* + * !!! NOTE !!! + * This function, if called in contexts other than vp create, disable + * or delete, please make sure this is synchronized with the + * delete thread. + */ fc_port_t *fcport; - scsi_qla_host_t *pha = to_qla_parent(vha); - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (fcport->vp_idx != vha->vp_idx) - continue; + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { + ql_dbg(ql_dbg_vport, vha, 0xa001, + "Marking port dead, loop_id=0x%04x : %x.\n", + fcport->loop_id, fcport->vha->vp_idx); - DEBUG15(printk("scsi(%ld): Marking port dead, " - "loop_id=0x%04x :%x\n", - vha->host_no, fcport->loop_id, fcport->vp_idx)); - - atomic_set(&fcport->state, FCS_DEVICE_DEAD); qla2x00_mark_device_lost(vha, fcport, 0, 0); + qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED); } } -int -qla24xx_disable_vp(scsi_qla_host_t *vha) +static int +__qla24xx_init_vp(scsi_qla_host_t *vha, int status) { - int ret; - - ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); atomic_set(&vha->loop_state, LOOP_DOWN); atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); #ifdef CONFIG_SCSI_QLA2XXX_TARGET /* Remove port id from vp target map */ - vha->parent->tgt_vp_map[vha->d_id.b.al_pa].idx = 0; -#endif + vha->hw->tgt_vp_map[vha->d_id.b.al_pa].idx = 0; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ /* Delete all vp's fcports from parent's list */ qla2x00_mark_vp_devices_dead(vha); atomic_set(&vha->vp_state, VP_FAILED); vha->flags.management_server_logged_in = 0; - if (ret == QLA_SUCCESS) { + if (status == QLA_SUCCESS) { fc_vport_set_state(vha->fc_vport, FC_VPORT_DISABLED); } else { fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); @@ -139,16 +173,55 @@ qla24xx_disable_vp(scsi_qla_host_t *vha) return 0; } +int +qla24xx_init_vp(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + /* + * When we are initing a virtual initiator port, it's a good idea to + * disable it at first to clean up all possible residues from another + * life of this virtual port on another physical port. But for virtual + * target port it's a pretty bad idea, because it can make initiators + * think that this port gone, hence disable it and fail all running IO. + * + * But this virtual port can be new in this place, so we might be not + * able to know if it's going to be initiator or target port. So, + * let's ask its parent as well, because good targets don't have + * initiator mode enabled anywhere. + */ + if (qla_ini_mode_enabled(base_vha)) + return qla24xx_disable_vp(vha); + else + return __qla24xx_init_vp(vha, QLA_SUCCESS); +} + +int +qla24xx_disable_vp(scsi_qla_host_t *vha) +{ + int ret; + + ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); + __qla24xx_init_vp(vha, ret); + if (ret != QLA_SUCCESS) + return -1; + else + return 0; +} +EXPORT_SYMBOL(qla24xx_disable_vp); + int qla24xx_enable_vp(scsi_qla_host_t *vha) { int ret; - scsi_qla_host_t *ha = vha->parent; + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); /* Check if physical ha port is Up */ - if (atomic_read(&ha->loop_state) == LOOP_DOWN || - atomic_read(&ha->loop_state) == LOOP_DEAD ) { - vha->vp_err_state = VP_ERR_PORTDWN; + if (atomic_read(&base_vha->loop_state) == LOOP_DOWN || + atomic_read(&base_vha->loop_state) == LOOP_DEAD || + !(ha->current_topology & ISP_CFG_F)) { + vha->vp_err_state = VP_ERR_PORTDWN; fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN); goto enable_failed; } @@ -163,15 +236,16 @@ qla24xx_enable_vp(scsi_qla_host_t *vha) goto enable_failed; } - DEBUG15(qla_printk(KERN_INFO, ha, - "Virtual port with id: %d - Enabled\n", vha->vp_idx)); + ql_dbg(ql_dbg_taskm, vha, 0x801a, + "Virtual port with id: %d - Enabled.\n", vha->vp_idx); return 0; enable_failed: - DEBUG15(qla_printk(KERN_INFO, ha, - "Virtual port with id: %d - Disabled\n", vha->vp_idx)); + ql_dbg(ql_dbg_taskm, vha, 0x801b, + "Virtual port with id: %d - Disabled.\n", vha->vp_idx); return 1; } +EXPORT_SYMBOL(qla24xx_enable_vp); static void qla24xx_configure_vp(scsi_qla_host_t *vha) @@ -181,16 +255,19 @@ qla24xx_configure_vp(scsi_qla_host_t *vha) fc_vport = vha->fc_vport; - DEBUG15(printk("scsi(%ld): %s: change request #3 for this host.\n", - vha->host_no, __func__)); - ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx); - if (ret != QLA_SUCCESS) { - DEBUG15(qla_printk(KERN_ERR, vha, "Failed to enable receiving" - " of RSCN requests: 0x%x\n", ret)); - return; - } else { - /* Corresponds to SCR enabled */ - clear_bit(VP_SCR_NEEDED, &vha->vp_flags); + /* Target mode has own handling */ + if (qla_ini_mode_enabled(vha)) { + ql_dbg(ql_dbg_vport, vha, 0xa002, + "%s: change request #3.\n", __func__); + ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx); + if (ret != QLA_SUCCESS) { + ql_dbg(ql_dbg_vport, vha, 0xa003, "Failed to enable " + "receiving of RSCN requests: 0x%x.\n", ret); + return; + } else { + /* Corresponds to SCR enabled */ + clear_bit(VP_SCR_NEEDED, &vha->vp_flags); + } } vha->flags.online = 1; @@ -202,25 +279,19 @@ qla24xx_configure_vp(scsi_qla_host_t *vha) } void -qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb) +qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb) { - int i, vp_idx_matched; scsi_qla_host_t *vha; + struct qla_hw_data *ha = rsp->hw; + int i = 0; + unsigned long flags; - if (ha->parent) - return; + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vha, &ha->vp_list, list) { + if (vha->vp_idx) { + atomic_inc(&vha->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); - for_each_mapped_vp_idx(ha, i) { - vp_idx_matched = 0; - - list_for_each_entry(vha, &ha->vp_list, vp_list) { - if (i == vha->vp_idx) { - vp_idx_matched = 1; - break; - } - } - - if (vp_idx_matched) { switch (mb[0]) { case MBA_LIP_OCCURRED: case MBA_LOOP_UP: @@ -230,19 +301,27 @@ qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb) case MBA_CHG_IN_CONNECTION: case MBA_PORT_UPDATE: case MBA_RSCN_UPDATE: - DEBUG15(printk("scsi(%ld)%s: Async_event for" - " VP[%d], mb = 0x%x, vha=%p\n", - vha->host_no, __func__,i, *mb, vha)); - qla2x00_async_event(vha, mb); + ql_dbg(ql_dbg_async, vha, 0x5024, + "Async_event for VP[%d], mb=0x%x vha=%p.\n", + i, *mb, vha); + qla2x00_async_event(vha, rsp, mb); break; } + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vha->vref_count); } + i++; } + spin_unlock_irqrestore(&ha->vport_slock, flags); } -void +int qla2x00_vp_abort_isp(scsi_qla_host_t *vha) { + int ret; + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); /* * Physical port will do most of the abort and recovery work. We can * just treat it as a loop down @@ -255,35 +334,66 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha) atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); } - DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n", - vha->host_no, vha->vp_idx)); - qla24xx_enable_vp(vha); + /* + * To exclusively reset vport, we need to log it out first. Note: this + * control_vp can fail if ISP reset is already issued, this is + * expected, as the vp would be already logged out due to ISP reset. + */ + if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) + qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); + + /* See comment in qla24xx_init_vp() */ + if (qla_ini_mode_enabled(base_vha)){ + ql_dbg(ql_dbg_taskm, vha, 0x801d, + "Scheduling enable of Vport %d.\n", vha->vp_idx); + ret = qla24xx_enable_vp(vha); + } else + ret = QLA_SUCCESS; #ifdef CONFIG_SCSI_QLA2XXX_TARGET /* Enable target response to SCSI bus. */ if (qla_tgt_mode_enabled(vha)) qla2x00_send_enable_lun(vha, true); -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + return ret; } static int qla2x00_do_dpc_vp(scsi_qla_host_t *vha) { - scsi_qla_host_t *ha = vha->parent; + ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x4012, + "Entering %s vp_flags: 0x%lx.\n", __func__, vha->vp_flags); + + qla2x00_do_work(vha); if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) { /* VP acquired. complete port configuration */ - if (atomic_read(&ha->loop_state) == LOOP_READY) { - qla24xx_configure_vp(vha); - } else { - set_bit(VP_IDX_ACQUIRED, &vha->vp_flags); - set_bit(VP_DPC_NEEDED, &ha->dpc_flags); - } - + ql_dbg(ql_dbg_dpc, vha, 0x4014, + "Configure VP scheduled.\n"); + qla24xx_configure_vp(vha); + ql_dbg(ql_dbg_dpc, vha, 0x4015, + "Configure VP end.\n"); return 0; } - if (test_and_clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) - qla2x00_vp_abort_isp(vha); + if (test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, vha, 0x4016, + "FCPort update scheduled.\n"); + qla2x00_update_fcports(vha); + clear_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags); + ql_dbg(ql_dbg_dpc, vha, 0x4017, + "FCPort update end.\n"); + } + + if ((test_and_clear_bit(RELOGIN_NEEDED, &vha->dpc_flags)) && + !test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) && + atomic_read(&vha->loop_state) != LOOP_DOWN) { + + ql_dbg(ql_dbg_dpc, vha, 0x4018, + "Relogin needed scheduled.\n"); + qla2x00_relogin(vha); + ql_dbg(ql_dbg_dpc, vha, 0x4019, + "Relogin needed end.\n"); + } if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) && (!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) { @@ -292,64 +402,79 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha) if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) { + ql_dbg(ql_dbg_dpc, vha, 0x401a, + "Loop resync scheduled.\n"); qla2x00_loop_resync(vha); clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags); + ql_dbg(ql_dbg_dpc, vha, 0x401b, + "Loop resync end.\n"); } } + ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x401c, + "Exiting %s.\n", __func__); return 0; } void -qla2x00_do_dpc_all_vps(scsi_qla_host_t *ha) +qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha) { int ret; - int i, vp_idx_matched; - scsi_qla_host_t *vha; + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *vp; + unsigned long flags = 0; - if (ha->parent) + if (vha->vp_idx) return; if (list_empty(&ha->vp_list)) return; - clear_bit(VP_DPC_NEEDED, &ha->dpc_flags); + clear_bit(VP_DPC_NEEDED, &vha->dpc_flags); - for_each_mapped_vp_idx(ha, i) { - vp_idx_matched = 0; + if (!(ha->current_topology & ISP_CFG_F)) + return; - list_for_each_entry(vha, &ha->vp_list, vp_list) { - if (i == vha->vp_idx) { - vp_idx_matched = 1; - break; - } + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + if (vp->vp_idx) { + atomic_inc(&vp->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); + + ret = qla2x00_do_dpc_vp(vp); + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vp->vref_count); } - - if (vp_idx_matched) - ret = qla2x00_do_dpc_vp(vha); } + spin_unlock_irqrestore(&ha->vport_slock, flags); } int qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport) { - scsi_qla_host_t *ha = shost_priv(fc_vport->shost); + scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost); + struct qla_hw_data *ha = base_vha->hw; scsi_qla_host_t *vha; uint8_t port_name[WWN_SIZE]; +#ifndef CONFIG_SCSI_QLA2XXX_TARGET if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR) return VPCERR_UNSUPPORTED; +#endif /* Check up the F/W and H/W support NPIV */ if (!ha->flags.npiv_supported) return VPCERR_UNSUPPORTED; +#if 0 /* we might not configured it yet */ /* Check up whether npiv supported switch presented */ if (!(ha->switch_cap & FLOGI_MID_SUPPORT)) return VPCERR_NO_FABRIC_SUPP; +#endif /* Check up unique WWPN */ u64_to_wwn(fc_vport->port_name, port_name); - if (!memcmp(port_name, ha->port_name, WWN_SIZE)) + if (!memcmp(port_name, base_vha->port_name, WWN_SIZE)) return VPCERR_BAD_WWN; vha = qla24xx_find_vhost_by_name(ha, port_name); if (vha) @@ -357,9 +482,9 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport) /* Check up max-npiv-supports */ if (ha->num_vhosts > ha->max_npiv_vports) { - DEBUG15(printk("scsi(%ld): num_vhosts %ud is bigger than " - "max_npv_vports %ud.\n", ha->host_no, - ha->num_vhosts, ha->max_npiv_vports)); + printk("scsi(%ld): num_vhosts %ud is bigger " + "than max_npiv_vports %ud.\n", base_vha->host_no, + ha->num_vhosts, ha->max_npiv_vports); return VPCERR_UNSUPPORTED; } return 0; @@ -368,70 +493,36 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport) scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *fc_vport) { - scsi_qla_host_t *ha = shost_priv(fc_vport->shost); + scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost); + struct qla_hw_data *ha = base_vha->hw; scsi_qla_host_t *vha; + struct scsi_host_template *sht = &qla2xxx_driver_template; struct Scsi_Host *host; - host = scsi_host_alloc(&qla24xx_driver_template, - sizeof(scsi_qla_host_t)); - if (!host) { - printk(KERN_WARNING - "qla2xxx: scsi_host_alloc() failed for vport\n"); + vha = qla2x00_create_host(sht, ha); + if (!vha) { + ql_log(ql_log_warn, vha, 0xa005, + "scsi_host_alloc() failed for vport.\n"); return(NULL); } - vha = shost_priv(host); - - /* clone the parent hba */ - memcpy(vha, ha, sizeof (scsi_qla_host_t)); - - memset(&vha->hardware_lock, 0, sizeof(spinlock_t)); - INIT_LIST_HEAD(&vha->vp_list); - spin_lock_init(&vha->dpc_lock); - mutex_init(&vha->tgt_mutex); - mutex_init(&vha->tgt_host_action_mutex); - INIT_LIST_HEAD(&vha->ha_list_entry); - + host = vha->host; fc_vport->dd_data = vha; - - vha->node_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL); - if (!vha->node_name) - goto create_vhost_failed_1; - - vha->port_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL); - if (!vha->port_name) - goto create_vhost_failed_2; - /* New host info */ u64_to_wwn(fc_vport->node_name, vha->node_name); u64_to_wwn(fc_vport->port_name, vha->port_name); - vha->host = host; - vha->host_no = host->host_no; - vha->parent = ha; vha->fc_vport = fc_vport; vha->device_flags = 0; - vha->instance = num_hosts; - qla24xx_allocate_vp_id(vha); + vha->vp_idx = qla24xx_allocate_vp_id(vha); if (vha->vp_idx > ha->max_npiv_vports) { - DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n", - vha->host_no)); - goto create_vhost_failed_3; + ql_dbg(ql_dbg_vport, vha, 0xa006, + "Couldn't allocate vp_id.\n"); + goto create_vhost_failed; } vha->mgmt_svr_loop_id = 10 + vha->vp_idx; - init_completion(&vha->mbx_cmd_comp); - complete(&vha->mbx_cmd_comp); - init_completion(&vha->mbx_intr_comp); - - INIT_LIST_HEAD(&vha->list); - INIT_LIST_HEAD(&vha->fcports); - INIT_LIST_HEAD(&vha->vp_fcports); - INIT_LIST_HEAD(&vha->work_list); - vha->dpc_flags = 0L; - set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); - set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); /* * To fix the issue of processing a parent's RSCN for the vport before @@ -443,21 +534,25 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL); - host->can_queue = vha->request_q_length + 128; + vha->req = base_vha->req; + host->can_queue = base_vha->req->length + 128; host->this_id = 255; host->cmd_per_lun = 3; - host->max_cmd_len = MAX_CMDSZ; + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) + host->max_cmd_len = 32; + else + host->max_cmd_len = MAX_CMDSZ; host->max_channel = MAX_BUSES - 1; - host->max_lun = MAX_LUNS; - host->unique_id = vha->instance; - host->max_id = MAX_TARGETS_2200; + host->max_lun = ql2xmaxlun; + host->unique_id = host->host_no; + host->max_id = ha->max_fibre_devices; host->transportt = qla2xxx_transport_vport_template; - DEBUG15(printk("DEBUG: detect vport hba %ld at address = %p\n", - vha->host_no, vha)); + ql_dbg(ql_dbg_vport, vha, 0xa007, + "Detect vport hba %ld at address = %p.\n", + vha->host_no, vha); vha->flags.init_done = 1; - num_hosts++; mutex_lock(&ha->vport_lock); set_bit(vha->vp_idx, ha->vp_idx_map); @@ -466,12 +561,342 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) return vha; -create_vhost_failed_3: - kfree(vha->port_name); - -create_vhost_failed_2: - kfree(vha->node_name); - -create_vhost_failed_1: +create_vhost_failed: return NULL; } + +static void +qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req) +{ + struct qla_hw_data *ha = vha->hw; + uint16_t que_id = req->id; + + dma_free_coherent(&ha->pdev->dev, (req->length + 1) * + sizeof(request_t), req->ring, req->dma); + req->ring = NULL; + req->dma = 0; + if (que_id) { + ha->req_q_map[que_id] = NULL; + mutex_lock(&ha->vport_lock); + clear_bit(que_id, ha->req_qid_map); + mutex_unlock(&ha->vport_lock); + } + kfree(req); + req = NULL; +} + +static void +qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) +{ + struct qla_hw_data *ha = vha->hw; + uint16_t que_id = rsp->id; + + if (rsp->msix && rsp->msix->have_irq) { + free_irq(rsp->msix->vector, rsp); + rsp->msix->have_irq = 0; + rsp->msix->rsp = NULL; + } + dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) * + sizeof(response_t), rsp->ring, rsp->dma); + rsp->ring = NULL; + rsp->dma = 0; + if (que_id) { + ha->rsp_q_map[que_id] = NULL; + mutex_lock(&ha->vport_lock); + clear_bit(que_id, ha->rsp_qid_map); + mutex_unlock(&ha->vport_lock); + } + kfree(rsp); + rsp = NULL; +} + +int +qla25xx_delete_req_que(struct scsi_qla_host *vha, struct req_que *req) +{ + int ret = -1; + + if (req) { + req->options |= BIT_0; + ret = qla25xx_init_req_que(vha, req); + } + if (ret == QLA_SUCCESS) + qla25xx_free_req_que(vha, req); + + return ret; +} + +static int +qla25xx_delete_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) +{ + int ret = -1; + + if (rsp) { + rsp->options |= BIT_0; + ret = qla25xx_init_rsp_que(vha, rsp); + } + if (ret == QLA_SUCCESS) + qla25xx_free_rsp_que(vha, rsp); + + return ret; +} + +/* Delete all queues for a given vhost */ +int +qla25xx_delete_queues(struct scsi_qla_host *vha) +{ + int cnt, ret = 0; + struct req_que *req = NULL; + struct rsp_que *rsp = NULL; + struct qla_hw_data *ha = vha->hw; + + /* Delete request queues */ + for (cnt = 1; cnt < ha->max_req_queues; cnt++) { + req = ha->req_q_map[cnt]; + if (req) { + ret = qla25xx_delete_req_que(vha, req); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x00ea, + "Couldn't delete req que %d.\n", + req->id); + return ret; + } + } + } + + /* Delete response queues */ + for (cnt = 1; cnt < ha->max_rsp_queues; cnt++) { + rsp = ha->rsp_q_map[cnt]; + if (rsp) { + ret = qla25xx_delete_rsp_que(vha, rsp); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x00eb, + "Couldn't delete rsp que %d.\n", + rsp->id); + return ret; + } + } + } + return ret; +} + +int +qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, + uint8_t vp_idx, uint16_t rid, int rsp_que, uint8_t qos) +{ + int ret = 0; + struct req_que *req = NULL; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + uint16_t que_id = 0; + device_reg_t __iomem *reg; + uint32_t cnt; + + req = kzalloc(sizeof(struct req_que), GFP_KERNEL); + if (req == NULL) { + ql_log(ql_log_fatal, base_vha, 0x00d9, + "Failed to allocate memory for request queue.\n"); + goto failed; + } + + req->length = REQUEST_ENTRY_CNT_24XX; + req->ring = dma_alloc_coherent(&ha->pdev->dev, + (req->length + 1) * sizeof(request_t), + &req->dma, GFP_KERNEL); + if (req->ring == NULL) { + ql_log(ql_log_fatal, base_vha, 0x00da, + "Failed to allocte memory for request_ring.\n"); + goto que_failed; + } + + mutex_lock(&ha->vport_lock); + que_id = find_first_zero_bit(ha->req_qid_map, ha->max_req_queues); + if (que_id >= ha->max_req_queues) { + mutex_unlock(&ha->vport_lock); + ql_log(ql_log_warn, base_vha, 0x00db, + "No resources to create additional request queue.\n"); + goto que_failed; + } + set_bit(que_id, ha->req_qid_map); + ha->req_q_map[que_id] = req; + req->rid = rid; + req->vp_idx = vp_idx; + req->qos = qos; + + ql_dbg(ql_dbg_multiq, base_vha, 0xc002, + "queue_id=%d rid=%d vp_idx=%d qos=%d.\n", + que_id, req->rid, req->vp_idx, req->qos); + ql_dbg(ql_dbg_init, base_vha, 0x00dc, + "queue_id=%d rid=%d vp_idx=%d qos=%d.\n", + que_id, req->rid, req->vp_idx, req->qos); + if (rsp_que < 0) + req->rsp = NULL; + else + req->rsp = ha->rsp_q_map[rsp_que]; + /* Use alternate PCI bus number */ + if (MSB(req->rid)) + options |= BIT_4; + /* Use alternate PCI devfn */ + if (LSB(req->rid)) + options |= BIT_5; + req->options = options; + + ql_dbg(ql_dbg_multiq, base_vha, 0xc003, + "options=0x%x.\n", req->options); + ql_dbg(ql_dbg_init, base_vha, 0x00dd, + "options=0x%x.\n", req->options); + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) + req->outstanding_cmds[cnt] = NULL; + req->current_outstanding_cmd = 1; + + req->ring_ptr = req->ring; + req->ring_index = 0; + req->cnt = req->length; + req->id = que_id; + reg = ISP_QUE_REG(ha, que_id); + req->max_q_depth = ha->req_q_map[0]->max_q_depth; + mutex_unlock(&ha->vport_lock); + ql_dbg(ql_dbg_multiq, base_vha, 0xc004, + "ring_ptr=%p ring_index=%d, " + "cnt=%d id=%d max_q_depth=%d.\n", + req->ring_ptr, req->ring_index, + req->cnt, req->id, req->max_q_depth); + ql_dbg(ql_dbg_init, base_vha, 0x00de, + "ring_ptr=%p ring_index=%d, " + "cnt=%d id=%d max_q_depth=%d.\n", + req->ring_ptr, req->ring_index, req->cnt, + req->id, req->max_q_depth); + + ret = qla25xx_init_req_que(base_vha, req); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_fatal, base_vha, 0x00df, + "%s failed.\n", __func__); + mutex_lock(&ha->vport_lock); + clear_bit(que_id, ha->req_qid_map); + mutex_unlock(&ha->vport_lock); + goto que_failed; + } + + return req->id; + +que_failed: + qla25xx_free_req_que(base_vha, req); +failed: + return 0; +} + +static void qla_do_work(struct work_struct *work) +{ + unsigned long flags; + struct rsp_que *rsp = container_of(work, struct rsp_que, q_work); + struct scsi_qla_host *vha; + struct qla_hw_data *ha = rsp->hw; + + spin_lock_irqsave(&rsp->hw->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + qla24xx_process_response_queue(vha, rsp); + spin_unlock_irqrestore(&rsp->hw->hardware_lock, flags); +} + +/* create response queue */ +int +qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, + uint8_t vp_idx, uint16_t rid, int req) +{ + int ret = 0; + struct rsp_que *rsp = NULL; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + uint16_t que_id = 0; + device_reg_t __iomem *reg; + + rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL); + if (rsp == NULL) { + ql_log(ql_log_warn, base_vha, 0x0066, + "Failed to allocate memory for response queue.\n"); + goto failed; + } + + rsp->length = RESPONSE_ENTRY_CNT_MQ; + rsp->ring = dma_alloc_coherent(&ha->pdev->dev, + (rsp->length + 1) * sizeof(response_t), + &rsp->dma, GFP_KERNEL); + if (rsp->ring == NULL) { + ql_log(ql_log_warn, base_vha, 0x00e1, + "Failed to allocate memory for response ring.\n"); + goto que_failed; + } + + mutex_lock(&ha->vport_lock); + que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues); + if (que_id >= ha->max_rsp_queues) { + mutex_unlock(&ha->vport_lock); + ql_log(ql_log_warn, base_vha, 0x00e2, + "No resources to create additional request queue.\n"); + goto que_failed; + } + set_bit(que_id, ha->rsp_qid_map); + + if (ha->flags.msix_enabled) + rsp->msix = &ha->msix_entries[que_id + 1]; + else + ql_log(ql_log_warn, base_vha, 0x00e3, + "MSIX not enalbled.\n"); + + ha->rsp_q_map[que_id] = rsp; + rsp->rid = rid; + rsp->vp_idx = vp_idx; + rsp->hw = ha; + ql_dbg(ql_dbg_init, base_vha, 0x00e4, + "queue_id=%d rid=%d vp_idx=%d hw=%p.\n", + que_id, rsp->rid, rsp->vp_idx, rsp->hw); + /* Use alternate PCI bus number */ + if (MSB(rsp->rid)) + options |= BIT_4; + /* Use alternate PCI devfn */ + if (LSB(rsp->rid)) + options |= BIT_5; + /* Enable MSIX handshake mode on for uncapable adapters */ + if (!IS_MSIX_NACK_CAPABLE(ha)) + options |= BIT_6; + + rsp->options = options; + rsp->id = que_id; + reg = ISP_QUE_REG(ha, que_id); + rsp->rsp_q_in = ®->isp25mq.rsp_q_in; + rsp->rsp_q_out = ®->isp25mq.rsp_q_out; + mutex_unlock(&ha->vport_lock); + ql_dbg(ql_dbg_multiq, base_vha, 0xc00b, + "options=%x id=%d rsp_q_in=%p rsp_q_out=%p", + rsp->options, rsp->id, rsp->rsp_q_in, + rsp->rsp_q_out); + ql_dbg(ql_dbg_init, base_vha, 0x00e5, + "options=%x id=%d rsp_q_in=%p rsp_q_out=%p", + rsp->options, rsp->id, rsp->rsp_q_in, + rsp->rsp_q_out); + + ret = qla25xx_request_irq(rsp); + if (ret) + goto que_failed; + + ret = qla25xx_init_rsp_que(base_vha, rsp); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_fatal, base_vha, 0x00e7, + "%s failed.\n", __func__); + mutex_lock(&ha->vport_lock); + clear_bit(que_id, ha->rsp_qid_map); + mutex_unlock(&ha->vport_lock); + goto que_failed; + } + if (req >= 0) + rsp->req = ha->req_q_map[req]; + else + rsp->req = NULL; + + qla2x00_init_response_q_entries(rsp); + if (rsp->hw->wq) + INIT_WORK(&rsp->q_work, qla_do_work); + return rsp->id; + +que_failed: + qla25xx_free_rsp_que(base_vha, rsp); +failed: + return 0; +} diff --git a/qla2x00t/qla_nx.c b/qla2x00t/qla_nx.c new file mode 100644 index 000000000..b0bad5671 --- /dev/null +++ b/qla2x00t/qla_nx.c @@ -0,0 +1,4463 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2011 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#include "qla_def.h" +#include +#include +#include +#include +#include + +#define MASK(n) ((1ULL<<(n))-1) +#define MN_WIN(addr) (((addr & 0x1fc0000) >> 1) | \ + ((addr >> 25) & 0x3ff)) +#define OCM_WIN(addr) (((addr & 0x1ff0000) >> 1) | \ + ((addr >> 25) & 0x3ff)) +#define MS_WIN(addr) (addr & 0x0ffc0000) +#define QLA82XX_PCI_MN_2M (0) +#define QLA82XX_PCI_MS_2M (0x80000) +#define QLA82XX_PCI_OCM0_2M (0xc0000) +#define VALID_OCM_ADDR(addr) (((addr) & 0x3f800) != 0x3f800) +#define GET_MEM_OFFS_2M(addr) (addr & MASK(18)) +#define BLOCK_PROTECT_BITS 0x0F + +/* CRB window related */ +#define CRB_BLK(off) ((off >> 20) & 0x3f) +#define CRB_SUBBLK(off) ((off >> 16) & 0xf) +#define CRB_WINDOW_2M (0x130060) +#define QLA82XX_PCI_CAMQM_2M_END (0x04800800UL) +#define CRB_HI(off) ((qla82xx_crb_hub_agt[CRB_BLK(off)] << 20) | \ + ((off) & 0xf0000)) +#define QLA82XX_PCI_CAMQM_2M_BASE (0x000ff800UL) +#define CRB_INDIRECT_2M (0x1e0000UL) + +#define MAX_CRB_XFORM 60 +static unsigned long crb_addr_xform[MAX_CRB_XFORM]; +int qla82xx_crb_table_initialized; + +#define qla82xx_crb_addr_transform(name) \ + (crb_addr_xform[QLA82XX_HW_PX_MAP_CRB_##name] = \ + QLA82XX_HW_CRB_HUB_AGT_ADR_##name << 20) + +static void qla82xx_crb_addr_transform_setup(void) +{ + qla82xx_crb_addr_transform(XDMA); + qla82xx_crb_addr_transform(TIMR); + qla82xx_crb_addr_transform(SRE); + qla82xx_crb_addr_transform(SQN3); + qla82xx_crb_addr_transform(SQN2); + qla82xx_crb_addr_transform(SQN1); + qla82xx_crb_addr_transform(SQN0); + qla82xx_crb_addr_transform(SQS3); + qla82xx_crb_addr_transform(SQS2); + qla82xx_crb_addr_transform(SQS1); + qla82xx_crb_addr_transform(SQS0); + qla82xx_crb_addr_transform(RPMX7); + qla82xx_crb_addr_transform(RPMX6); + qla82xx_crb_addr_transform(RPMX5); + qla82xx_crb_addr_transform(RPMX4); + qla82xx_crb_addr_transform(RPMX3); + qla82xx_crb_addr_transform(RPMX2); + qla82xx_crb_addr_transform(RPMX1); + qla82xx_crb_addr_transform(RPMX0); + qla82xx_crb_addr_transform(ROMUSB); + qla82xx_crb_addr_transform(SN); + qla82xx_crb_addr_transform(QMN); + qla82xx_crb_addr_transform(QMS); + qla82xx_crb_addr_transform(PGNI); + qla82xx_crb_addr_transform(PGND); + qla82xx_crb_addr_transform(PGN3); + qla82xx_crb_addr_transform(PGN2); + qla82xx_crb_addr_transform(PGN1); + qla82xx_crb_addr_transform(PGN0); + qla82xx_crb_addr_transform(PGSI); + qla82xx_crb_addr_transform(PGSD); + qla82xx_crb_addr_transform(PGS3); + qla82xx_crb_addr_transform(PGS2); + qla82xx_crb_addr_transform(PGS1); + qla82xx_crb_addr_transform(PGS0); + qla82xx_crb_addr_transform(PS); + qla82xx_crb_addr_transform(PH); + qla82xx_crb_addr_transform(NIU); + qla82xx_crb_addr_transform(I2Q); + qla82xx_crb_addr_transform(EG); + qla82xx_crb_addr_transform(MN); + qla82xx_crb_addr_transform(MS); + qla82xx_crb_addr_transform(CAS2); + qla82xx_crb_addr_transform(CAS1); + qla82xx_crb_addr_transform(CAS0); + qla82xx_crb_addr_transform(CAM); + qla82xx_crb_addr_transform(C2C1); + qla82xx_crb_addr_transform(C2C0); + qla82xx_crb_addr_transform(SMB); + qla82xx_crb_addr_transform(OCM0); + /* + * Used only in P3 just define it for P2 also. + */ + qla82xx_crb_addr_transform(I2C0); + + qla82xx_crb_table_initialized = 1; +} + +struct crb_128M_2M_block_map crb_128M_2M_map[64] = { + {{{0, 0, 0, 0} } }, + {{{1, 0x0100000, 0x0102000, 0x120000}, + {1, 0x0110000, 0x0120000, 0x130000}, + {1, 0x0120000, 0x0122000, 0x124000}, + {1, 0x0130000, 0x0132000, 0x126000}, + {1, 0x0140000, 0x0142000, 0x128000}, + {1, 0x0150000, 0x0152000, 0x12a000}, + {1, 0x0160000, 0x0170000, 0x110000}, + {1, 0x0170000, 0x0172000, 0x12e000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x01e0000, 0x01e0800, 0x122000}, + {0, 0x0000000, 0x0000000, 0x000000} } } , + {{{1, 0x0200000, 0x0210000, 0x180000} } }, + {{{0, 0, 0, 0} } }, + {{{1, 0x0400000, 0x0401000, 0x169000} } }, + {{{1, 0x0500000, 0x0510000, 0x140000} } }, + {{{1, 0x0600000, 0x0610000, 0x1c0000} } }, + {{{1, 0x0700000, 0x0704000, 0x1b8000} } }, + {{{1, 0x0800000, 0x0802000, 0x170000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x08f0000, 0x08f2000, 0x172000} } }, + {{{1, 0x0900000, 0x0902000, 0x174000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x09f0000, 0x09f2000, 0x176000} } }, + {{{0, 0x0a00000, 0x0a02000, 0x178000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x0af0000, 0x0af2000, 0x17a000} } }, + {{{0, 0x0b00000, 0x0b02000, 0x17c000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x0bf0000, 0x0bf2000, 0x17e000} } }, + {{{1, 0x0c00000, 0x0c04000, 0x1d4000} } }, + {{{1, 0x0d00000, 0x0d04000, 0x1a4000} } }, + {{{1, 0x0e00000, 0x0e04000, 0x1a0000} } }, + {{{1, 0x0f00000, 0x0f01000, 0x164000} } }, + {{{0, 0x1000000, 0x1004000, 0x1a8000} } }, + {{{1, 0x1100000, 0x1101000, 0x160000} } }, + {{{1, 0x1200000, 0x1201000, 0x161000} } }, + {{{1, 0x1300000, 0x1301000, 0x162000} } }, + {{{1, 0x1400000, 0x1401000, 0x163000} } }, + {{{1, 0x1500000, 0x1501000, 0x165000} } }, + {{{1, 0x1600000, 0x1601000, 0x166000} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{1, 0x1d00000, 0x1d10000, 0x190000} } }, + {{{1, 0x1e00000, 0x1e01000, 0x16a000} } }, + {{{1, 0x1f00000, 0x1f10000, 0x150000} } }, + {{{0} } }, + {{{1, 0x2100000, 0x2102000, 0x120000}, + {1, 0x2110000, 0x2120000, 0x130000}, + {1, 0x2120000, 0x2122000, 0x124000}, + {1, 0x2130000, 0x2132000, 0x126000}, + {1, 0x2140000, 0x2142000, 0x128000}, + {1, 0x2150000, 0x2152000, 0x12a000}, + {1, 0x2160000, 0x2170000, 0x110000}, + {1, 0x2170000, 0x2172000, 0x12e000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000} } }, + {{{1, 0x2200000, 0x2204000, 0x1b0000} } }, + {{{0} } }, + {{{0} } }, + {{{0} } }, + {{{0} } }, + {{{0} } }, + {{{1, 0x2800000, 0x2804000, 0x1a4000} } }, + {{{1, 0x2900000, 0x2901000, 0x16b000} } }, + {{{1, 0x2a00000, 0x2a00400, 0x1ac400} } }, + {{{1, 0x2b00000, 0x2b00400, 0x1ac800} } }, + {{{1, 0x2c00000, 0x2c00400, 0x1acc00} } }, + {{{1, 0x2d00000, 0x2d00400, 0x1ad000} } }, + {{{1, 0x2e00000, 0x2e00400, 0x1ad400} } }, + {{{1, 0x2f00000, 0x2f00400, 0x1ad800} } }, + {{{1, 0x3000000, 0x3000400, 0x1adc00} } }, + {{{0, 0x3100000, 0x3104000, 0x1a8000} } }, + {{{1, 0x3200000, 0x3204000, 0x1d4000} } }, + {{{1, 0x3300000, 0x3304000, 0x1a0000} } }, + {{{0} } }, + {{{1, 0x3500000, 0x3500400, 0x1ac000} } }, + {{{1, 0x3600000, 0x3600400, 0x1ae000} } }, + {{{1, 0x3700000, 0x3700400, 0x1ae400} } }, + {{{1, 0x3800000, 0x3804000, 0x1d0000} } }, + {{{1, 0x3900000, 0x3904000, 0x1b4000} } }, + {{{1, 0x3a00000, 0x3a04000, 0x1d8000} } }, + {{{0} } }, + {{{0} } }, + {{{1, 0x3d00000, 0x3d04000, 0x1dc000} } }, + {{{1, 0x3e00000, 0x3e01000, 0x167000} } }, + {{{1, 0x3f00000, 0x3f01000, 0x168000} } } +}; + +/* + * top 12 bits of crb internal address (hub, agent) + */ +unsigned qla82xx_crb_hub_agt[64] = +{ + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PS, + QLA82XX_HW_CRB_HUB_AGT_ADR_MN, + QLA82XX_HW_CRB_HUB_AGT_ADR_MS, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_SRE, + QLA82XX_HW_CRB_HUB_AGT_ADR_NIU, + QLA82XX_HW_CRB_HUB_AGT_ADR_QMN, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN0, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN1, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN2, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN3, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q, + QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR, + QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN4, + QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN1, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN2, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN3, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGND, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGNI, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS1, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS2, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS3, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGSI, + QLA82XX_HW_CRB_HUB_AGT_ADR_SN, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_EG, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PS, + QLA82XX_HW_CRB_HUB_AGT_ADR_CAM, + 0, + 0, + 0, + 0, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX1, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX2, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX3, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX4, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX5, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX6, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX7, + QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q, + QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX0, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX8, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX9, + QLA82XX_HW_CRB_HUB_AGT_ADR_OCM0, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_SMB, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2C0, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2C1, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGNC, + 0, +}; + +/* Device states */ +char *q_dev_state[] = { + "Unknown", + "Cold", + "Initializing", + "Ready", + "Need Reset", + "Need Quiescent", + "Failed", + "Quiescent", +}; + +char *qdev_state(uint32_t dev_state) +{ + return q_dev_state[dev_state]; +} + +/* + * In: 'off' is offset from CRB space in 128M pci map + * Out: 'off' is 2M pci map addr + * side effect: lock crb window + */ +static void +qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong *off) +{ + u32 win_read; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + ha->crb_win = CRB_HI(*off); + writel(ha->crb_win, + (void *)(CRB_WINDOW_2M + ha->nx_pcibase)); + + /* Read back value to make sure write has gone through before trying + * to use it. + */ + win_read = RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase)); + if (win_read != ha->crb_win) { + ql_dbg(ql_dbg_p3p, vha, 0xb000, + "%s: Written crbwin (0x%x) " + "!= Read crbwin (0x%x), off=0x%lx.\n", + __func__, ha->crb_win, win_read, *off); + } + *off = (*off & MASK(16)) + CRB_INDIRECT_2M + ha->nx_pcibase; +} + +static inline unsigned long +qla82xx_pci_set_crbwindow(struct qla_hw_data *ha, u64 off) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + /* See if we are currently pointing to the region we want to use next */ + if ((off >= QLA82XX_CRB_PCIX_HOST) && (off < QLA82XX_CRB_DDR_NET)) { + /* No need to change window. PCIX and PCIEregs are in both + * regs are in both windows. + */ + return off; + } + + if ((off >= QLA82XX_CRB_PCIX_HOST) && (off < QLA82XX_CRB_PCIX_HOST2)) { + /* We are in first CRB window */ + if (ha->curr_window != 0) + WARN_ON(1); + return off; + } + + if ((off > QLA82XX_CRB_PCIX_HOST2) && (off < QLA82XX_CRB_MAX)) { + /* We are in second CRB window */ + off = off - QLA82XX_CRB_PCIX_HOST2 + QLA82XX_CRB_PCIX_HOST; + + if (ha->curr_window != 1) + return off; + + /* We are in the QM or direct access + * register region - do nothing + */ + if ((off >= QLA82XX_PCI_DIRECT_CRB) && + (off < QLA82XX_PCI_CAMQM_MAX)) + return off; + } + /* strange address given */ + ql_dbg(ql_dbg_p3p, vha, 0xb001, + "%s: Warning: unm_nic_pci_set_crbwindow " + "called with an unknown address(%llx).\n", + QLA2XXX_DRIVER_NAME, off); + return off; +} + +#define CRB_WIN_LOCK_TIMEOUT 100000000 +static int +qla82xx_crb_win_lock(struct qla_hw_data *ha) +{ + int i; + int done = 0, timeout = 0; + + while (!done) { + /* acquire semaphore3 from PCI HW block */ + done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_LOCK)); + if (done == 1) + break; + if (timeout >= CRB_WIN_LOCK_TIMEOUT) + return -1; + timeout++; + + /* Yield CPU */ + if (!in_atomic()) + schedule(); + else { + for (i = 0; i < 20; i++) + cpu_relax(); + } + } + qla82xx_wr_32(ha, QLA82XX_CRB_WIN_LOCK_ID, ha->portnum); + return 0; +} + +static int +qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong *off) +{ + struct crb_128M_2M_sub_block_map *m; + + if (*off >= QLA82XX_CRB_MAX) + return -1; + + if (*off >= QLA82XX_PCI_CAMQM && (*off < QLA82XX_PCI_CAMQM_2M_END)) { + *off = (*off - QLA82XX_PCI_CAMQM) + + QLA82XX_PCI_CAMQM_2M_BASE + ha->nx_pcibase; + return 0; + } + + if (*off < QLA82XX_PCI_CRBSPACE) + return -1; + + *off -= QLA82XX_PCI_CRBSPACE; + + /* Try direct map */ + m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)]; + + if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) { + *off = *off + m->start_2M - m->start_128M + ha->nx_pcibase; + return 0; + } + /* Not in direct map, use crb window */ + return 1; +} + +int +qla82xx_wr_32(struct qla_hw_data *ha, ulong off, u32 data) +{ + unsigned long flags = 0; + int rv; + + rv = qla82xx_pci_get_crb_addr_2M(ha, &off); + + BUG_ON(rv == -1); + + if (rv == 1) { + write_lock_irqsave(&ha->hw_lock, flags); + qla82xx_crb_win_lock(ha); + qla82xx_pci_set_crbwindow_2M(ha, &off); + } + + writel(data, (void __iomem *)off); + + if (rv == 1) { + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK)); + write_unlock_irqrestore(&ha->hw_lock, flags); + } + return 0; +} + +int +qla82xx_rd_32(struct qla_hw_data *ha, ulong off) +{ + unsigned long flags = 0; + int rv; + u32 data; + + rv = qla82xx_pci_get_crb_addr_2M(ha, &off); + + BUG_ON(rv == -1); + + if (rv == 1) { + write_lock_irqsave(&ha->hw_lock, flags); + qla82xx_crb_win_lock(ha); + qla82xx_pci_set_crbwindow_2M(ha, &off); + } + data = RD_REG_DWORD((void __iomem *)off); + + if (rv == 1) { + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK)); + write_unlock_irqrestore(&ha->hw_lock, flags); + } + return data; +} + +#define IDC_LOCK_TIMEOUT 100000000 +int +qla82xx_idc_lock(struct qla_hw_data *ha) +{ + int i; + int done = 0, timeout = 0; + + while (!done) { + /* acquire semaphore5 from PCI HW block */ + done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_LOCK)); + if (done == 1) + break; + if (timeout >= IDC_LOCK_TIMEOUT) + return -1; + + timeout++; + + /* Yield CPU */ + if (!in_interrupt()) + schedule(); + else { + for (i = 0; i < 20; i++) + cpu_relax(); + } + } + + return 0; +} + +void +qla82xx_idc_unlock(struct qla_hw_data *ha) +{ + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_UNLOCK)); +} + +/* PCI Windowing for DDR regions. */ +static inline bool QLA82XX_ADDR_IN_RANGE(uintptr_t addr, uintptr_t low, + uintptr_t high) +{ + return addr <= high && addr >= low; +} + +/* + * check memory access boundary. + * used by test agent. support ddr access only for now + */ +static unsigned long +qla82xx_pci_mem_bound_check(struct qla_hw_data *ha, + unsigned long long addr, int size) +{ + if (!QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX) || + !QLA82XX_ADDR_IN_RANGE(addr + size - 1, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX) || + ((size != 1) && (size != 2) && (size != 4) && (size != 8))) + return 0; + else + return 1; +} + +int qla82xx_pci_set_window_warning_count; + +static unsigned long +qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) +{ + int window; + u32 win_read; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX)) { + /* DDR network side */ + window = MN_WIN(addr); + ha->ddr_mn_window = window; + qla82xx_wr_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE, window); + win_read = qla82xx_rd_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE); + if ((win_read << 17) != window) { + ql_dbg(ql_dbg_p3p, vha, 0xb003, + "%s: Written MNwin (0x%x) != Read MNwin (0x%x).\n", + __func__, window, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_DDR_NET; + } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0, + QLA82XX_ADDR_OCM0_MAX)) { + unsigned int temp1; + if ((addr & 0x00ff800) == 0xff800) { + ql_log(ql_log_warn, vha, 0xb004, + "%s: QM access not handled.\n", __func__); + addr = -1UL; + } + window = OCM_WIN(addr); + ha->ddr_mn_window = window; + qla82xx_wr_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE, window); + win_read = qla82xx_rd_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE); + temp1 = ((window & 0x1FF) << 7) | + ((window & 0x0FFFE0000) >> 17); + if (win_read != temp1) { + ql_log(ql_log_warn, vha, 0xb005, + "%s: Written OCMwin (0x%x) != Read OCMwin (0x%x).\n", + __func__, temp1, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_OCM0_2M; + + } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET, + QLA82XX_P3_ADDR_QDR_NET_MAX)) { + /* QDR network side */ + window = MS_WIN(addr); + ha->qdr_sn_window = window; + qla82xx_wr_32(ha, + ha->ms_win_crb | QLA82XX_PCI_CRBSPACE, window); + win_read = qla82xx_rd_32(ha, + ha->ms_win_crb | QLA82XX_PCI_CRBSPACE); + if (win_read != window) { + ql_log(ql_log_warn, vha, 0xb006, + "%s: Written MSwin (0x%x) != Read MSwin (0x%x).\n", + __func__, window, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_QDR_NET; + } else { + /* + * peg gdb frequently accesses memory that doesn't exist, + * this limits the chit chat so debugging isn't slowed down. + */ + if ((qla82xx_pci_set_window_warning_count++ < 8) || + (qla82xx_pci_set_window_warning_count%64 == 0)) { + ql_log(ql_log_warn, vha, 0xb007, + "%s: Warning:%s Unknown address range!.\n", + __func__, QLA2XXX_DRIVER_NAME); + } + addr = -1UL; + } + return addr; +} + +/* check if address is in the same windows as the previous access */ +static int +qla82xx_pci_is_same_window(struct qla_hw_data *ha, + unsigned long long addr) +{ + int window; + unsigned long long qdr_max; + + qdr_max = QLA82XX_P3_ADDR_QDR_NET_MAX; + + /* DDR network side */ + if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX)) + BUG(); + else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0, + QLA82XX_ADDR_OCM0_MAX)) + return 1; + else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM1, + QLA82XX_ADDR_OCM1_MAX)) + return 1; + else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET, qdr_max)) { + /* QDR network side */ + window = ((addr - QLA82XX_ADDR_QDR_NET) >> 22) & 0x3f; + if (ha->qdr_sn_window == window) + return 1; + } + return 0; +} + +static int +qla82xx_pci_mem_read_direct(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + unsigned long flags; + void *addr = NULL; + int ret = 0; + u64 start; + uint8_t *mem_ptr = NULL; + unsigned long mem_base; + unsigned long mem_page; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + write_lock_irqsave(&ha->hw_lock, flags); + + /* + * If attempting to access unknown address or straddle hw windows, + * do not access. + */ + start = qla82xx_pci_set_window(ha, off); + if ((start == -1UL) || + (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) { + write_unlock_irqrestore(&ha->hw_lock, flags); + ql_log(ql_log_fatal, vha, 0xb008, + "%s out of bound pci memory " + "access, offset is 0x%llx.\n", + QLA2XXX_DRIVER_NAME, off); + return -1; + } + + write_unlock_irqrestore(&ha->hw_lock, flags); + mem_base = pci_resource_start(ha->pdev, 0); + mem_page = start & PAGE_MASK; + /* Map two pages whenever user tries to access addresses in two + * consecutive pages. + */ + if (mem_page != ((start + size - 1) & PAGE_MASK)) + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2); + else + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); + if (mem_ptr == 0UL) { + *(u8 *)data = 0; + return -1; + } + addr = mem_ptr; + addr += start & (PAGE_SIZE - 1); + write_lock_irqsave(&ha->hw_lock, flags); + + switch (size) { + case 1: + *(u8 *)data = readb(addr); + break; + case 2: + *(u16 *)data = readw(addr); + break; + case 4: + *(u32 *)data = readl(addr); + break; + case 8: + *(u64 *)data = readq(addr); + break; + default: + ret = -1; + break; + } + write_unlock_irqrestore(&ha->hw_lock, flags); + + if (mem_ptr) + iounmap(mem_ptr); + return ret; +} + +static int +qla82xx_pci_mem_write_direct(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + unsigned long flags; + void *addr = NULL; + int ret = 0; + u64 start; + uint8_t *mem_ptr = NULL; + unsigned long mem_base; + unsigned long mem_page; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + write_lock_irqsave(&ha->hw_lock, flags); + + /* + * If attempting to access unknown address or straddle hw windows, + * do not access. + */ + start = qla82xx_pci_set_window(ha, off); + if ((start == -1UL) || + (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) { + write_unlock_irqrestore(&ha->hw_lock, flags); + ql_log(ql_log_fatal, vha, 0xb009, + "%s out of bount memory " + "access, offset is 0x%llx.\n", + QLA2XXX_DRIVER_NAME, off); + return -1; + } + + write_unlock_irqrestore(&ha->hw_lock, flags); + mem_base = pci_resource_start(ha->pdev, 0); + mem_page = start & PAGE_MASK; + /* Map two pages whenever user tries to access addresses in two + * consecutive pages. + */ + if (mem_page != ((start + size - 1) & PAGE_MASK)) + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2); + else + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); + if (mem_ptr == 0UL) + return -1; + + addr = mem_ptr; + addr += start & (PAGE_SIZE - 1); + write_lock_irqsave(&ha->hw_lock, flags); + + switch (size) { + case 1: + writeb(*(u8 *)data, addr); + break; + case 2: + writew(*(u16 *)data, addr); + break; + case 4: + writel(*(u32 *)data, addr); + break; + case 8: + writeq(*(u64 *)data, addr); + break; + default: + ret = -1; + break; + } + write_unlock_irqrestore(&ha->hw_lock, flags); + if (mem_ptr) + iounmap(mem_ptr); + return ret; +} + +#define MTU_FUDGE_FACTOR 100 +static unsigned long +qla82xx_decode_crb_addr(unsigned long addr) +{ + int i; + unsigned long base_addr, offset, pci_base; + + if (!qla82xx_crb_table_initialized) + qla82xx_crb_addr_transform_setup(); + + pci_base = ADDR_ERROR; + base_addr = addr & 0xfff00000; + offset = addr & 0x000fffff; + + for (i = 0; i < MAX_CRB_XFORM; i++) { + if (crb_addr_xform[i] == base_addr) { + pci_base = i << 20; + break; + } + } + if (pci_base == ADDR_ERROR) + return pci_base; + return pci_base + offset; +} + +static long rom_max_timeout = 100; +static long qla82xx_rom_lock_timeout = 100; + +static int +qla82xx_rom_lock(struct qla_hw_data *ha) +{ + int done = 0, timeout = 0; + + while (!done) { + /* acquire semaphore2 from PCI HW block */ + done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_LOCK)); + if (done == 1) + break; + if (timeout >= qla82xx_rom_lock_timeout) + return -1; + timeout++; + } + qla82xx_wr_32(ha, QLA82XX_ROM_LOCK_ID, ROM_LOCK_DRIVER); + return 0; +} + +static void +qla82xx_rom_unlock(struct qla_hw_data *ha) +{ + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); +} + +static int +qla82xx_wait_rom_busy(struct qla_hw_data *ha) +{ + long timeout = 0; + long done = 0 ; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + while (done == 0) { + done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS); + done &= 4; + timeout++; + if (timeout >= rom_max_timeout) { + ql_dbg(ql_dbg_p3p, vha, 0xb00a, + "%s: Timeout reached waiting for rom busy.\n", + QLA2XXX_DRIVER_NAME); + return -1; + } + } + return 0; +} + +static int +qla82xx_wait_rom_done(struct qla_hw_data *ha) +{ + long timeout = 0; + long done = 0 ; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + while (done == 0) { + done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS); + done &= 2; + timeout++; + if (timeout >= rom_max_timeout) { + ql_dbg(ql_dbg_p3p, vha, 0xb00b, + "%s: Timeout reached waiting for rom done.\n", + QLA2XXX_DRIVER_NAME); + return -1; + } + } + return 0; +} + +int +qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag) +{ + uint32_t off_value, rval = 0; + + WRT_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase), + (off & 0xFFFF0000)); + + /* Read back value to make sure write has gone through */ + RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase)); + off_value = (off & 0x0000FFFF); + + if (flag) + WRT_REG_DWORD((void *) + (off_value + CRB_INDIRECT_2M + ha->nx_pcibase), + data); + else + rval = RD_REG_DWORD((void *) + (off_value + CRB_INDIRECT_2M + ha->nx_pcibase)); + + return rval; +} + +static int +qla82xx_do_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) +{ + /* Dword reads to flash. */ + qla82xx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW, (addr & 0xFFFF0000), 1); + *valp = qla82xx_md_rw_32(ha, MD_DIRECT_ROM_READ_BASE + + (addr & 0x0000FFFF), 0, 0); + + return 0; +} + +static int +qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) +{ + int ret, loops = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) { + udelay(100); + schedule(); + loops++; + } + if (loops >= 50000) { + ql_log(ql_log_fatal, vha, 0x00b9, + "Failed to aquire SEM2 lock.\n"); + return -1; + } + ret = qla82xx_do_rom_fast_read(ha, addr, valp); + qla82xx_rom_unlock(ha); + return ret; +} + +static int +qla82xx_read_status_reg(struct qla_hw_data *ha, uint32_t *val) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_RDSR); + qla82xx_wait_rom_busy(ha); + if (qla82xx_wait_rom_done(ha)) { + ql_log(ql_log_warn, vha, 0xb00c, + "Error waiting for rom done.\n"); + return -1; + } + *val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA); + return 0; +} + +static int +qla82xx_flash_wait_write_finish(struct qla_hw_data *ha) +{ + long timeout = 0; + uint32_t done = 1 ; + uint32_t val; + int ret = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0); + while ((done != 0) && (ret == 0)) { + ret = qla82xx_read_status_reg(ha, &val); + done = val & 1; + timeout++; + udelay(10); + cond_resched(); + if (timeout >= 50000) { + ql_log(ql_log_warn, vha, 0xb00d, + "Timeout reached waiting for write finish.\n"); + return -1; + } + } + return ret; +} + +static int +qla82xx_flash_set_write_enable(struct qla_hw_data *ha) +{ + uint32_t val; + qla82xx_wait_rom_busy(ha); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WREN); + qla82xx_wait_rom_busy(ha); + if (qla82xx_wait_rom_done(ha)) + return -1; + if (qla82xx_read_status_reg(ha, &val) != 0) + return -1; + if ((val & 2) != 2) + return -1; + return 0; +} + +static int +qla82xx_write_status_reg(struct qla_hw_data *ha, uint32_t val) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + if (qla82xx_flash_set_write_enable(ha)) + return -1; + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, val); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0x1); + if (qla82xx_wait_rom_done(ha)) { + ql_log(ql_log_warn, vha, 0xb00e, + "Error waiting for rom done.\n"); + return -1; + } + return qla82xx_flash_wait_write_finish(ha); +} + +static int +qla82xx_write_disable_flash(struct qla_hw_data *ha) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WRDI); + if (qla82xx_wait_rom_done(ha)) { + ql_log(ql_log_warn, vha, 0xb00f, + "Error waiting for rom done.\n"); + return -1; + } + return 0; +} + +static int +ql82xx_rom_lock_d(struct qla_hw_data *ha) +{ + int loops = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) { + udelay(100); + cond_resched(); + loops++; + } + if (loops >= 50000) { + ql_log(ql_log_warn, vha, 0xb010, + "ROM lock failed.\n"); + return -1; + } + return 0; +} + +static int +qla82xx_write_flash_dword(struct qla_hw_data *ha, uint32_t flashaddr, + uint32_t data) +{ + int ret = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + ql_log(ql_log_warn, vha, 0xb011, + "ROM lock failed.\n"); + return ret; + } + + if (qla82xx_flash_set_write_enable(ha)) + goto done_write; + + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, data); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, flashaddr); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_PP); + qla82xx_wait_rom_busy(ha); + if (qla82xx_wait_rom_done(ha)) { + ql_log(ql_log_warn, vha, 0xb012, + "Error waiting for rom done.\n"); + ret = -1; + goto done_write; + } + + ret = qla82xx_flash_wait_write_finish(ha); + +done_write: + qla82xx_rom_unlock(ha); + return ret; +} + +/* This routine does CRB initialize sequence + * to put the ISP into operational state + */ +static int +qla82xx_pinit_from_rom(scsi_qla_host_t *vha) +{ + int addr, val; + int i ; + struct crb_addr_pair *buf; + unsigned long off; + unsigned offset, n; + struct qla_hw_data *ha = vha->hw; + + struct crb_addr_pair { + long addr; + long data; + }; + + /* Halt all the indiviual PEGs and other blocks of the ISP */ + qla82xx_rom_lock(ha); + + /* disable all I2Q */ + qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x10, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x14, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x18, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x1c, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x20, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x24, 0x0); + + /* disable all niu interrupts */ + qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x40, 0xff); + /* disable xge rx/tx */ + qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x70000, 0x00); + /* disable xg1 rx/tx */ + qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x80000, 0x00); + /* disable sideband mac */ + qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x90000, 0x00); + /* disable ap0 mac */ + qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0xa0000, 0x00); + /* disable ap1 mac */ + qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0xb0000, 0x00); + + /* halt sre */ + val = qla82xx_rd_32(ha, QLA82XX_CRB_SRE + 0x1000); + qla82xx_wr_32(ha, QLA82XX_CRB_SRE + 0x1000, val & (~(0x1))); + + /* halt epg */ + qla82xx_wr_32(ha, QLA82XX_CRB_EPG + 0x1300, 0x1); + + /* halt timers */ + qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x0, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x8, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x10, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x18, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x100, 0x0); + qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x200, 0x0); + + /* halt pegs */ + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x3c, 1); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1 + 0x3c, 1); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2 + 0x3c, 1); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3 + 0x3c, 1); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_4 + 0x3c, 1); + msleep(5); + + /* big hammer */ + if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) + /* don't reset CAM block on reset */ + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xfeffffff); + else + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xffffffff); + qla82xx_rom_unlock(ha); + + /* Read the signature value from the flash. + * Offset 0: Contain signature (0xcafecafe) + * Offset 4: Offset and number of addr/value pairs + * that present in CRB initialize sequence + */ + if (qla82xx_rom_fast_read(ha, 0, &n) != 0 || n != 0xcafecafeUL || + qla82xx_rom_fast_read(ha, 4, &n) != 0) { + ql_log(ql_log_fatal, vha, 0x006e, + "Error Reading crb_init area: n: %08x.\n", n); + return -1; + } + + /* Offset in flash = lower 16 bits + * Number of enteries = upper 16 bits + */ + offset = n & 0xffffU; + n = (n >> 16) & 0xffffU; + + /* number of addr/value pair should not exceed 1024 entries */ + if (n >= 1024) { + ql_log(ql_log_fatal, vha, 0x0071, + "Card flash not initialized:n=0x%x.\n", n); + return -1; + } + + ql_log(ql_log_info, vha, 0x0072, + "%d CRB init values found in ROM.\n", n); + + buf = kmalloc(n * sizeof(struct crb_addr_pair), GFP_KERNEL); + if (buf == NULL) { + ql_log(ql_log_fatal, vha, 0x010c, + "Unable to allocate memory.\n"); + return -1; + } + + for (i = 0; i < n; i++) { + if (qla82xx_rom_fast_read(ha, 8*i + 4*offset, &val) != 0 || + qla82xx_rom_fast_read(ha, 8*i + 4*offset + 4, &addr) != 0) { + kfree(buf); + return -1; + } + + buf[i].addr = addr; + buf[i].data = val; + } + + for (i = 0; i < n; i++) { + /* Translate internal CRB initialization + * address to PCI bus address + */ + off = qla82xx_decode_crb_addr((unsigned long)buf[i].addr) + + QLA82XX_PCI_CRBSPACE; + /* Not all CRB addr/value pair to be written, + * some of them are skipped + */ + + /* skipping cold reboot MAGIC */ + if (off == QLA82XX_CAM_RAM(0x1fc)) + continue; + + /* do not reset PCI */ + if (off == (ROMUSB_GLB + 0xbc)) + continue; + + /* skip core clock, so that firmware can increase the clock */ + if (off == (ROMUSB_GLB + 0xc8)) + continue; + + /* skip the function enable register */ + if (off == QLA82XX_PCIE_REG(PCIE_SETUP_FUNCTION)) + continue; + + if (off == QLA82XX_PCIE_REG(PCIE_SETUP_FUNCTION2)) + continue; + + if ((off & 0x0ff00000) == QLA82XX_CRB_SMB) + continue; + + if ((off & 0x0ff00000) == QLA82XX_CRB_DDR_NET) + continue; + + if (off == ADDR_ERROR) { + ql_log(ql_log_fatal, vha, 0x0116, + "Unknow addr: 0x%08lx.\n", buf[i].addr); + continue; + } + + qla82xx_wr_32(ha, off, buf[i].data); + + /* ISP requires much bigger delay to settle down, + * else crb_window returns 0xffffffff + */ + if (off == QLA82XX_ROMUSB_GLB_SW_RESET) + msleep(1000); + + /* ISP requires millisec delay between + * successive CRB register updates + */ + msleep(1); + } + + kfree(buf); + + /* Resetting the data and instruction cache */ + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_D+0xec, 0x1e); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_D+0x4c, 8); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_I+0x4c, 8); + + /* Clear all protocol processing engines */ + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0+0xc, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1+0xc, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2+0xc, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3+0xc, 0); + return 0; +} + +static int +qla82xx_pci_mem_read_2M(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + int i, j = 0, k, start, end, loop, sz[2], off0[2]; + int shift_amount; + uint32_t temp; + uint64_t off8, val, mem_crb, word[2] = {0, 0}; + + /* + * If not MN, go check for MS or invalid. + */ + + if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX) + mem_crb = QLA82XX_CRB_QDR_NET; + else { + mem_crb = QLA82XX_CRB_DDR_NET; + if (qla82xx_pci_mem_bound_check(ha, off, size) == 0) + return qla82xx_pci_mem_read_direct(ha, + off, data, size); + } + + off8 = off & 0xfffffff0; + off0[0] = off & 0xf; + sz[0] = (size < (16 - off0[0])) ? size : (16 - off0[0]); + shift_amount = 4; + loop = ((off0[0] + size - 1) >> shift_amount) + 1; + off0[1] = 0; + sz[1] = size - sz[0]; + + for (i = 0; i < loop; i++) { + temp = off8 + (i << shift_amount); + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_LO, temp); + temp = 0; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_HI, temp); + temp = MIU_TA_CTL_ENABLE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&ha->pdev->dev, + "failed to read through agent\n"); + break; + } + + start = off0[i] >> 2; + end = (off0[i] + sz[i] - 1) >> 2; + for (k = start; k <= end; k++) { + temp = qla82xx_rd_32(ha, + mem_crb + MIU_TEST_AGT_RDDATA(k)); + word[i] |= ((uint64_t)temp << (32 * (k & 1))); + } + } + + if (j >= MAX_CTL_CHECK) + return -1; + + if ((off0[0] & 7) == 0) { + val = word[0]; + } else { + val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | + ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); + } + + switch (size) { + case 1: + *(uint8_t *)data = val; + break; + case 2: + *(uint16_t *)data = val; + break; + case 4: + *(uint32_t *)data = val; + break; + case 8: + *(uint64_t *)data = val; + break; + } + return 0; +} + +static int +qla82xx_pci_mem_write_2M(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + int i, j, ret = 0, loop, sz[2], off0; + int scale, shift_amount, startword; + uint32_t temp; + uint64_t off8, mem_crb, tmpw, word[2] = {0, 0}; + + /* + * If not MN, go check for MS or invalid. + */ + if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX) + mem_crb = QLA82XX_CRB_QDR_NET; + else { + mem_crb = QLA82XX_CRB_DDR_NET; + if (qla82xx_pci_mem_bound_check(ha, off, size) == 0) + return qla82xx_pci_mem_write_direct(ha, + off, data, size); + } + + off0 = off & 0x7; + sz[0] = (size < (8 - off0)) ? size : (8 - off0); + sz[1] = size - sz[0]; + + off8 = off & 0xfffffff0; + loop = (((off & 0xf) + size - 1) >> 4) + 1; + shift_amount = 4; + scale = 2; + startword = (off & 0xf)/8; + + for (i = 0; i < loop; i++) { + if (qla82xx_pci_mem_read_2M(ha, off8 + + (i << shift_amount), &word[i * scale], 8)) + return -1; + } + + switch (size) { + case 1: + tmpw = *((uint8_t *)data); + break; + case 2: + tmpw = *((uint16_t *)data); + break; + case 4: + tmpw = *((uint32_t *)data); + break; + case 8: + default: + tmpw = *((uint64_t *)data); + break; + } + + if (sz[0] == 8) { + word[startword] = tmpw; + } else { + word[startword] &= + ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); + word[startword] |= tmpw << (off0 * 8); + } + if (sz[1] != 0) { + word[startword+1] &= ~(~0ULL << (sz[1] * 8)); + word[startword+1] |= tmpw >> (sz[0] * 8); + } + + for (i = 0; i < loop; i++) { + temp = off8 + (i << shift_amount); + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_LO, temp); + temp = 0; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_HI, temp); + temp = word[i * scale] & 0xffffffff; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_LO, temp); + temp = (word[i * scale] >> 32) & 0xffffffff; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_HI, temp); + temp = word[i*scale + 1] & 0xffffffff; + qla82xx_wr_32(ha, mem_crb + + MIU_TEST_AGT_WRDATA_UPPER_LO, temp); + temp = (word[i*scale + 1] >> 32) & 0xffffffff; + qla82xx_wr_32(ha, mem_crb + + MIU_TEST_AGT_WRDATA_UPPER_HI, temp); + + temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&ha->pdev->dev, + "failed to write through agent.\n"); + ret = -1; + break; + } + } + + return ret; +} + +static int +qla82xx_fw_load_from_flash(struct qla_hw_data *ha) +{ + int i; + long size = 0; + long flashaddr = ha->flt_region_bootload << 2; + long memaddr = BOOTLD_START; + u64 data; + u32 high, low; + size = (IMAGE_START - BOOTLD_START) / 8; + + for (i = 0; i < size; i++) { + if ((qla82xx_rom_fast_read(ha, flashaddr, (int *)&low)) || + (qla82xx_rom_fast_read(ha, flashaddr + 4, (int *)&high))) { + return -1; + } + data = ((u64)high << 32) | low ; + qla82xx_pci_mem_write_2M(ha, memaddr, &data, 8); + flashaddr += 8; + memaddr += 8; + + if (i % 0x1000 == 0) + msleep(1); + } + udelay(100); + read_lock(&ha->hw_lock); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x18, 0x1020); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001e); + read_unlock(&ha->hw_lock); + return 0; +} + +static struct qla82xx_uri_table_desc * +qla82xx_get_table_desc(const u8 *unirom, int section) +{ + uint32_t i; + struct qla82xx_uri_table_desc *directory = + (struct qla82xx_uri_table_desc *)&unirom[0]; + __le32 offset; + __le32 tab_type; + __le32 entries = cpu_to_le32(directory->num_entries); + + for (i = 0; i < entries; i++) { + offset = cpu_to_le32(directory->findex) + + (i * cpu_to_le32(directory->entry_size)); + tab_type = cpu_to_le32(*((u32 *)&unirom[offset] + 8)); + + if (tab_type == section) + return (struct qla82xx_uri_table_desc *)&unirom[offset]; + } + + return NULL; +} + +static struct qla82xx_uri_data_desc * +qla82xx_get_data_desc(struct qla_hw_data *ha, + u32 section, u32 idx_offset) +{ + const u8 *unirom = ha->hablob->fw->data; + int idx = cpu_to_le32(*((int *)&unirom[ha->file_prd_off] + idx_offset)); + struct qla82xx_uri_table_desc *tab_desc = NULL; + __le32 offset; + + tab_desc = qla82xx_get_table_desc(unirom, section); + if (!tab_desc) + return NULL; + + offset = cpu_to_le32(tab_desc->findex) + + (cpu_to_le32(tab_desc->entry_size) * idx); + + return (struct qla82xx_uri_data_desc *)&unirom[offset]; +} + +static u8 * +qla82xx_get_bootld_offset(struct qla_hw_data *ha) +{ + u32 offset = BOOTLD_START; + struct qla82xx_uri_data_desc *uri_desc = NULL; + + if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) { + uri_desc = qla82xx_get_data_desc(ha, + QLA82XX_URI_DIR_SECT_BOOTLD, QLA82XX_URI_BOOTLD_IDX_OFF); + if (uri_desc) + offset = cpu_to_le32(uri_desc->findex); + } + + return (u8 *)&ha->hablob->fw->data[offset]; +} + +static __le32 +qla82xx_get_fw_size(struct qla_hw_data *ha) +{ + struct qla82xx_uri_data_desc *uri_desc = NULL; + + if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) { + uri_desc = qla82xx_get_data_desc(ha, QLA82XX_URI_DIR_SECT_FW, + QLA82XX_URI_FIRMWARE_IDX_OFF); + if (uri_desc) + return cpu_to_le32(uri_desc->size); + } + + return cpu_to_le32(*(u32 *)&ha->hablob->fw->data[FW_SIZE_OFFSET]); +} + +static u8 * +qla82xx_get_fw_offs(struct qla_hw_data *ha) +{ + u32 offset = IMAGE_START; + struct qla82xx_uri_data_desc *uri_desc = NULL; + + if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) { + uri_desc = qla82xx_get_data_desc(ha, QLA82XX_URI_DIR_SECT_FW, + QLA82XX_URI_FIRMWARE_IDX_OFF); + if (uri_desc) + offset = cpu_to_le32(uri_desc->findex); + } + + return (u8 *)&ha->hablob->fw->data[offset]; +} + +/* PCI related functions */ +char * +qla82xx_pci_info_str(struct scsi_qla_host *vha, char *str, int str_len) +{ + int pcie_reg; + struct qla_hw_data *ha = vha->hw; + char lwstr[6]; + uint16_t lnk; + + pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); + pci_read_config_word(ha->pdev, pcie_reg + PCI_EXP_LNKSTA, &lnk); + ha->link_width = (lnk >> 4) & 0x3f; + + strlcpy(str, "PCIe (", str_len); + strncat(str, "2.5Gb/s ",str_len - (strlen(str)+1)); + snprintf(lwstr, sizeof(lwstr), "x%d)", ha->link_width); + strncat(str, lwstr, str_len - (strlen(str)+1)); + return str; +} + +int +qla82xx_iospace_config(struct qla_hw_data *ha) +{ + uint32_t len = 0; + + if (pci_request_regions(ha->pdev, QLA2XXX_DRIVER_NAME)) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x000c, + "Failed to reserver selected regions.\n"); + goto iospace_error_exit; + } + + /* Use MMIO operations for all accesses. */ + if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x000d, + "Region #0 not an MMIO resource, aborting.\n"); + goto iospace_error_exit; + } + + len = pci_resource_len(ha->pdev, 0); + ha->nx_pcibase = + (unsigned long)ioremap(pci_resource_start(ha->pdev, 0), len); + if (!ha->nx_pcibase) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x000e, + "Cannot remap pcibase MMIO, aborting.\n"); + pci_release_regions(ha->pdev); + goto iospace_error_exit; + } + + /* Mapping of IO base pointer */ + ha->iobase = (device_reg_t __iomem *)((uint8_t *)ha->nx_pcibase + + 0xbc000 + (ha->pdev->devfn << 11)); + + if (!ql2xdbwr) { + ha->nxdb_wr_ptr = + (unsigned long)ioremap((pci_resource_start(ha->pdev, 4) + + (ha->pdev->devfn << 12)), 4); + if (!ha->nxdb_wr_ptr) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x000f, + "Cannot remap MMIO, aborting.\n"); + pci_release_regions(ha->pdev); + goto iospace_error_exit; + } + + /* Mapping of IO base pointer, + * door bell read and write pointer + */ + ha->nxdb_rd_ptr = (uint8_t *) ha->nx_pcibase + (512 * 1024) + + (ha->pdev->devfn * 8); + } else { + ha->nxdb_wr_ptr = (ha->pdev->devfn == 6 ? + QLA82XX_CAMRAM_DB1 : + QLA82XX_CAMRAM_DB2); + } + + ha->max_req_queues = ha->max_rsp_queues = 1; + ha->msix_count = ha->max_rsp_queues + 1; + ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc006, + "nx_pci_base=%p iobase=%p " + "max_req_queues=%d msix_count=%d.\n", + (void *)ha->nx_pcibase, ha->iobase, + ha->max_req_queues, ha->msix_count); + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0010, + "nx_pci_base=%p iobase=%p " + "max_req_queues=%d msix_count=%d.\n", + (void *)ha->nx_pcibase, ha->iobase, + ha->max_req_queues, ha->msix_count); + return 0; + +iospace_error_exit: + return -ENOMEM; +} + +/* GS related functions */ + +/* Initialization related functions */ + +/** + * qla82xx_pci_config() - Setup ISP82xx PCI configuration registers. + * @ha: HA context + * + * Returns 0 on success. +*/ +int +qla82xx_pci_config(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + int ret; + + pci_set_master(ha->pdev); + ret = pci_set_mwi(ha->pdev); + ha->chip_revision = ha->pdev->revision; + ql_dbg(ql_dbg_init, vha, 0x0043, + "Chip revision:%d.\n", + ha->chip_revision); + return 0; +} + +/** + * qla82xx_reset_chip() - Setup ISP82xx PCI configuration registers. + * @ha: HA context + * + * Returns 0 on success. + */ +void +qla82xx_reset_chip(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + ha->isp_ops->disable_intrs(ha); +} + +void +qla82xx_config_rings(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; + struct init_cb_81xx *icb; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; + + /* Setup ring parameters in initialization control block. */ + icb = (struct init_cb_81xx *)ha->init_cb; + icb->request_q_outpointer = __constant_cpu_to_le16(0); + icb->response_q_inpointer = __constant_cpu_to_le16(0); + icb->request_q_length = cpu_to_le16(req->length); + icb->response_q_length = cpu_to_le16(rsp->length); + icb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); + icb->request_q_address[1] = cpu_to_le32(MSD(req->dma)); + icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma)); + icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma)); + + WRT_REG_DWORD((unsigned long __iomem *)®->req_q_out[0], 0); + WRT_REG_DWORD((unsigned long __iomem *)®->rsp_q_in[0], 0); + WRT_REG_DWORD((unsigned long __iomem *)®->rsp_q_out[0], 0); +} + +void qla82xx_reset_adapter(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + vha->flags.online = 0; + qla2x00_try_to_stop_firmware(vha); + ha->isp_ops->disable_intrs(ha); +} + +static int +qla82xx_fw_load_from_blob(struct qla_hw_data *ha) +{ + u64 *ptr64; + u32 i, flashaddr, size; + __le64 data; + + size = (IMAGE_START - BOOTLD_START) / 8; + + ptr64 = (u64 *)qla82xx_get_bootld_offset(ha); + flashaddr = BOOTLD_START; + + for (i = 0; i < size; i++) { + data = cpu_to_le64(ptr64[i]); + if (qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8)) + return -EIO; + flashaddr += 8; + } + udelay(100); + + flashaddr = FLASH_ADDR_START; + size = (__force u32)qla82xx_get_fw_size(ha) / 8; + ptr64 = (u64 *)qla82xx_get_fw_offs(ha); + + for (i = 0; i < size; i++) { + data = cpu_to_le64(ptr64[i]); + + if (qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8)) + return -EIO; + flashaddr += 8; + } + + /* Write a magic value to CAMRAM register + * at a specified offset to indicate + * that all data is written and + * ready for firmware to initialize. + */ + qla82xx_wr_32(ha, QLA82XX_CAM_RAM(0x1fc), QLA82XX_BDINFO_MAGIC); + read_lock(&ha->hw_lock); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x18, 0x1020); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001e); + read_unlock(&ha->hw_lock); + return 0; +} + +static int +qla82xx_set_product_offset(struct qla_hw_data *ha) +{ + struct qla82xx_uri_table_desc *ptab_desc = NULL; + const uint8_t *unirom = ha->hablob->fw->data; + uint32_t i; + __le32 entries; + __le32 flags, file_chiprev, offset; + uint8_t chiprev = ha->chip_revision; + /* Hardcoding mn_present flag for P3P */ + int mn_present = 0; + uint32_t flagbit; + + ptab_desc = qla82xx_get_table_desc(unirom, + QLA82XX_URI_DIR_SECT_PRODUCT_TBL); + if (!ptab_desc) + return -1; + + entries = cpu_to_le32(ptab_desc->num_entries); + + for (i = 0; i < entries; i++) { + offset = cpu_to_le32(ptab_desc->findex) + + (i * cpu_to_le32(ptab_desc->entry_size)); + flags = cpu_to_le32(*((int *)&unirom[offset] + + QLA82XX_URI_FLAGS_OFF)); + file_chiprev = cpu_to_le32(*((int *)&unirom[offset] + + QLA82XX_URI_CHIP_REV_OFF)); + + flagbit = mn_present ? 1 : 2; + + if ((chiprev == file_chiprev) && ((1ULL << flagbit) & flags)) { + ha->file_prd_off = offset; + return 0; + } + } + return -1; +} + +int +qla82xx_validate_firmware_blob(scsi_qla_host_t *vha, uint8_t fw_type) +{ + __le32 val; + uint32_t min_size; + struct qla_hw_data *ha = vha->hw; + const struct firmware *fw = ha->hablob->fw; + + ha->fw_type = fw_type; + + if (fw_type == QLA82XX_UNIFIED_ROMIMAGE) { + if (qla82xx_set_product_offset(ha)) + return -EINVAL; + + min_size = QLA82XX_URI_FW_MIN_SIZE; + } else { + val = cpu_to_le32(*(u32 *)&fw->data[QLA82XX_FW_MAGIC_OFFSET]); + if ((__force u32)val != QLA82XX_BDINFO_MAGIC) + return -EINVAL; + + min_size = QLA82XX_FW_MIN_SIZE; + } + + if (fw->size < min_size) + return -EINVAL; + return 0; +} + +static int +qla82xx_check_cmdpeg_state(struct qla_hw_data *ha) +{ + u32 val = 0; + int retries = 60; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + do { + read_lock(&ha->hw_lock); + val = qla82xx_rd_32(ha, CRB_CMDPEG_STATE); + read_unlock(&ha->hw_lock); + + switch (val) { + case PHAN_INITIALIZE_COMPLETE: + case PHAN_INITIALIZE_ACK: + return QLA_SUCCESS; + case PHAN_INITIALIZE_FAILED: + break; + default: + break; + } + ql_log(ql_log_info, vha, 0x00a8, + "CRB_CMDPEG_STATE: 0x%x and retries:0x%x.\n", + val, retries); + + msleep(500); + + } while (--retries); + + ql_log(ql_log_fatal, vha, 0x00a9, + "Cmd Peg initialization failed: 0x%x.\n", val); + + val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_PEGTUNE_DONE); + read_lock(&ha->hw_lock); + qla82xx_wr_32(ha, CRB_CMDPEG_STATE, PHAN_INITIALIZE_FAILED); + read_unlock(&ha->hw_lock); + return QLA_FUNCTION_FAILED; +} + +static int +qla82xx_check_rcvpeg_state(struct qla_hw_data *ha) +{ + u32 val = 0; + int retries = 60; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + do { + read_lock(&ha->hw_lock); + val = qla82xx_rd_32(ha, CRB_RCVPEG_STATE); + read_unlock(&ha->hw_lock); + + switch (val) { + case PHAN_INITIALIZE_COMPLETE: + case PHAN_INITIALIZE_ACK: + return QLA_SUCCESS; + case PHAN_INITIALIZE_FAILED: + break; + default: + break; + } + ql_log(ql_log_info, vha, 0x00ab, + "CRB_RCVPEG_STATE: 0x%x and retries: 0x%x.\n", + val, retries); + + msleep(500); + + } while (--retries); + + ql_log(ql_log_fatal, vha, 0x00ac, + "Rcv Peg initializatin failed: 0x%x.\n", val); + read_lock(&ha->hw_lock); + qla82xx_wr_32(ha, CRB_RCVPEG_STATE, PHAN_INITIALIZE_FAILED); + read_unlock(&ha->hw_lock); + return QLA_FUNCTION_FAILED; +} + +/* ISR related functions */ +uint32_t qla82xx_isr_int_target_mask_enable[8] = { + ISR_INT_TARGET_MASK, ISR_INT_TARGET_MASK_F1, + ISR_INT_TARGET_MASK_F2, ISR_INT_TARGET_MASK_F3, + ISR_INT_TARGET_MASK_F4, ISR_INT_TARGET_MASK_F5, + ISR_INT_TARGET_MASK_F7, ISR_INT_TARGET_MASK_F7 +}; + +uint32_t qla82xx_isr_int_target_status[8] = { + ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1, + ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3, + ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5, + ISR_INT_TARGET_STATUS_F7, ISR_INT_TARGET_STATUS_F7 +}; + +static struct qla82xx_legacy_intr_set legacy_intr[] = \ + QLA82XX_LEGACY_INTR_CONFIG; + +/* + * qla82xx_mbx_completion() - Process mailbox command completions. + * @ha: SCSI driver HA context + * @mb0: Mailbox0 register + */ +static void +qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) +{ + uint16_t cnt; + uint16_t __iomem *wptr; + struct qla_hw_data *ha = vha->hw; + struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; + wptr = (uint16_t __iomem *)®->mailbox_out[1]; + + /* Load return mailbox registers. */ + ha->flags.mbox_int = 1; + ha->mailbox_out[0] = mb0; + + for (cnt = 1; cnt < ha->mbx_count; cnt++) { + ha->mailbox_out[cnt] = RD_REG_WORD(wptr); + wptr++; + } + + if (!ha->mcp) + ql_dbg(ql_dbg_async, vha, 0x5053, + "MBX pointer ERROR.\n"); +} + +/* + * qla82xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. + * @irq: + * @dev_id: SCSI driver HA context + * @regs: + * + * Called by system whenever the host adapter generates an interrupt. + * + * Returns handled flag. + */ +irqreturn_t +qla82xx_intr_handler(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + int status = 0, status1 = 0; + unsigned long flags; + unsigned long iter; + uint32_t stat = 0; + uint16_t mb[4]; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0xb053, + "%s: NULL response queue pointer.\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; + + if (!ha->flags.msi_enabled) { + status = qla82xx_rd_32(ha, ISR_INT_VECTOR); + if (!(status & ha->nx_legacy_intr.int_vec_bit)) + return IRQ_NONE; + + status1 = qla82xx_rd_32(ha, ISR_INT_STATE_REG); + if (!ISR_IS_LEGACY_INTR_TRIGGERED(status1)) + return IRQ_NONE; + } + + /* clear the interrupt */ + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff); + + /* read twice to ensure write is flushed */ + qla82xx_rd_32(ha, ISR_INT_VECTOR); + qla82xx_rd_32(ha, ISR_INT_VECTOR); + + reg = &ha->iobase->isp82; + + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + for (iter = 1; iter--; ) { + + if (RD_REG_DWORD(®->host_int)) { + stat = RD_REG_DWORD(®->host_status); + + switch (stat & 0xff) { + case 0x1: + case 0x2: + case 0x10: + case 0x11: + qla82xx_mbx_completion(vha, MSW(stat)); + status |= MBX_INTERRUPT; + break; + case 0x12: + mb[0] = MSW(stat); + mb[1] = RD_REG_WORD(®->mailbox_out[1]); + mb[2] = RD_REG_WORD(®->mailbox_out[2]); + mb[3] = RD_REG_WORD(®->mailbox_out[3]); + qla2x00_async_event(vha, rsp, mb); + break; + case 0x13: + qla24xx_process_response_queue(vha, rsp); + break; + default: + ql_dbg(ql_dbg_async, vha, 0x5054, + "Unrecognized interrupt type (%d).\n", + stat & 0xff); + break; + } + } + WRT_REG_DWORD(®->host_int, 0); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (!ha->flags.msi_enabled) + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); + +#ifdef QL_DEBUG_LEVEL_17 + if (!irq && ha->flags.eeh_busy) + ql_log(ql_log_warn, vha, 0x503d, + "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n", + status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat); +#endif + + if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && + (status & MBX_INTERRUPT) && ha->flags.mbox_int) { + set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + complete(&ha->mbx_intr_comp); + } + return IRQ_HANDLED; +} + +irqreturn_t +qla82xx_msix_default(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + int status = 0; + unsigned long flags; + uint32_t stat = 0; + uint16_t mb[4]; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + printk(KERN_INFO + "%s(): NULL response queue pointer.\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; + + reg = &ha->iobase->isp82; + + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + do { + if (RD_REG_DWORD(®->host_int)) { + stat = RD_REG_DWORD(®->host_status); + + switch (stat & 0xff) { + case 0x1: + case 0x2: + case 0x10: + case 0x11: + qla82xx_mbx_completion(vha, MSW(stat)); + status |= MBX_INTERRUPT; + break; + case 0x12: + mb[0] = MSW(stat); + mb[1] = RD_REG_WORD(®->mailbox_out[1]); + mb[2] = RD_REG_WORD(®->mailbox_out[2]); + mb[3] = RD_REG_WORD(®->mailbox_out[3]); + qla2x00_async_event(vha, rsp, mb); + break; + case 0x13: + qla24xx_process_response_queue(vha, rsp); + break; + default: + ql_dbg(ql_dbg_async, vha, 0x5041, + "Unrecognized interrupt type (%d).\n", + stat & 0xff); + break; + } + } + WRT_REG_DWORD(®->host_int, 0); + } while (0); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +#ifdef QL_DEBUG_LEVEL_17 + if (!irq && ha->flags.eeh_busy) + ql_log(ql_log_warn, vha, 0x5044, + "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n", + status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat); +#endif + + if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && + (status & MBX_INTERRUPT) && ha->flags.mbox_int) { + set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + complete(&ha->mbx_intr_comp); + } + return IRQ_HANDLED; +} + +irqreturn_t +qla82xx_msix_rsp_q(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + unsigned long flags; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + printk(KERN_INFO + "%s(): NULL response queue pointer.\n", __func__); + return IRQ_NONE; + } + + ha = rsp->hw; + reg = &ha->iobase->isp82; + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + qla24xx_process_response_queue(vha, rsp); + WRT_REG_DWORD(®->host_int, 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return IRQ_HANDLED; +} + +void +qla82xx_poll(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + int status = 0; + uint32_t stat; + uint16_t mb[4]; + unsigned long flags; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + printk(KERN_INFO + "%s(): NULL response queue pointer.\n", __func__); + return; + } + ha = rsp->hw; + + reg = &ha->iobase->isp82; + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + + if (RD_REG_DWORD(®->host_int)) { + stat = RD_REG_DWORD(®->host_status); + switch (stat & 0xff) { + case 0x1: + case 0x2: + case 0x10: + case 0x11: + qla82xx_mbx_completion(vha, MSW(stat)); + status |= MBX_INTERRUPT; + break; + case 0x12: + mb[0] = MSW(stat); + mb[1] = RD_REG_WORD(®->mailbox_out[1]); + mb[2] = RD_REG_WORD(®->mailbox_out[2]); + mb[3] = RD_REG_WORD(®->mailbox_out[3]); + qla2x00_async_event(vha, rsp, mb); + break; + case 0x13: + qla24xx_process_response_queue(vha, rsp); + break; + default: + ql_dbg(ql_dbg_p3p, vha, 0xb013, + "Unrecognized interrupt type (%d).\n", + stat * 0xff); + break; + } + } + WRT_REG_DWORD(®->host_int, 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void +qla82xx_enable_intrs(struct qla_hw_data *ha) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + qla82xx_mbx_intr_enable(vha); + spin_lock_irq(&ha->hardware_lock); + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); + spin_unlock_irq(&ha->hardware_lock); + ha->interrupts_on = 1; +} + +void +qla82xx_disable_intrs(struct qla_hw_data *ha) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + qla82xx_mbx_intr_disable(vha); + spin_lock_irq(&ha->hardware_lock); + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0x0400); + spin_unlock_irq(&ha->hardware_lock); + ha->interrupts_on = 0; +} + +void +qla82xx_init_flags(struct qla_hw_data *ha) +{ + struct qla82xx_legacy_intr_set *nx_legacy_intr; + + /* ISP 8021 initializations */ + rwlock_init(&ha->hw_lock); + ha->qdr_sn_window = -1; + ha->ddr_mn_window = -1; + ha->curr_window = 255; + ha->portnum = PCI_FUNC(ha->pdev->devfn); + nx_legacy_intr = &legacy_intr[ha->portnum]; + ha->nx_legacy_intr.int_vec_bit = nx_legacy_intr->int_vec_bit; + ha->nx_legacy_intr.tgt_status_reg = nx_legacy_intr->tgt_status_reg; + ha->nx_legacy_intr.tgt_mask_reg = nx_legacy_intr->tgt_mask_reg; + ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg; +} + +inline void +qla82xx_set_drv_active(scsi_qla_host_t *vha) +{ + uint32_t drv_active; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + + /* If reset value is all FF's, initialize DRV_ACTIVE */ + if (drv_active == 0xffffffff) { + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, + QLA82XX_DRV_NOT_ACTIVE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + } + drv_active |= (QLA82XX_DRV_ACTIVE << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active); +} + +inline void +qla82xx_clear_drv_active(struct qla_hw_data *ha) +{ + uint32_t drv_active; + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + drv_active &= ~(QLA82XX_DRV_ACTIVE << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active); +} + +static inline int +qla82xx_need_reset(struct qla_hw_data *ha) +{ + uint32_t drv_state; + int rval; + + if (ha->flags.isp82xx_reset_owner) + return 1; + else { + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); + return rval; + } +} + +static inline void +qla82xx_set_rst_ready(struct qla_hw_data *ha) +{ + uint32_t drv_state; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + + /* If reset value is all FF's, initialize DRV_STATE */ + if (drv_state == 0xffffffff) { + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, QLA82XX_DRVST_NOT_RDY); + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + } + drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); + ql_dbg(ql_dbg_init, vha, 0x00bb, + "drv_state = 0x%08x.\n", drv_state); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state); +} + +static inline void +qla82xx_clear_rst_ready(struct qla_hw_data *ha) +{ + uint32_t drv_state; + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_state &= ~(QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state); +} + +static inline void +qla82xx_set_qsnt_ready(struct qla_hw_data *ha) +{ + uint32_t qsnt_state; + + qsnt_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + qsnt_state |= (QLA82XX_DRVST_QSNT_RDY << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state); +} + +void +qla82xx_clear_qsnt_ready(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t qsnt_state; + + qsnt_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + qsnt_state &= ~(QLA82XX_DRVST_QSNT_RDY << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state); +} + +static int +qla82xx_load_fw(scsi_qla_host_t *vha) +{ + int rst; + struct fw_blob *blob; + struct qla_hw_data *ha = vha->hw; + + if (qla82xx_pinit_from_rom(vha) != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x009f, + "Error during CRB initialization.\n"); + return QLA_FUNCTION_FAILED; + } + udelay(500); + + /* Bring QM and CAMRAM out of reset */ + rst = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET); + rst &= ~((1 << 28) | (1 << 24)); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, rst); + + /* + * FW Load priority: + * 1) Operational firmware residing in flash. + * 2) Firmware via request-firmware interface (.bin file). + */ + if (ql2xfwloadbin == 2) + goto try_blob_fw; + + ql_log(ql_log_info, vha, 0x00a0, + "Attempting to load firmware from flash.\n"); + + if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) { + ql_log(ql_log_info, vha, 0x00a1, + "Firmware loaded successfully from flash.\n"); + return QLA_SUCCESS; + } else { + ql_log(ql_log_warn, vha, 0x0108, + "Firmware load from flash failed.\n"); + } +try_blob_fw: + ql_log(ql_log_info, vha, 0x00a2, + "Attempting to load firmware from blob.\n"); + + /* Load firmware blob. */ + blob = ha->hablob = qla2x00_request_firmware(vha); + if (!blob) { + ql_log(ql_log_fatal, vha, 0x00a3, + "Firmware image not present.\n"); + goto fw_load_failed; + } + + /* Validating firmware blob */ + if (qla82xx_validate_firmware_blob(vha, + QLA82XX_FLASH_ROMIMAGE)) { + /* Fallback to URI format */ + if (qla82xx_validate_firmware_blob(vha, + QLA82XX_UNIFIED_ROMIMAGE)) { + ql_log(ql_log_fatal, vha, 0x00a4, + "No valid firmware image found.\n"); + return QLA_FUNCTION_FAILED; + } + } + + if (qla82xx_fw_load_from_blob(ha) == QLA_SUCCESS) { + ql_log(ql_log_info, vha, 0x00a5, + "Firmware loaded successfully from binary blob.\n"); + return QLA_SUCCESS; + } else { + ql_log(ql_log_fatal, vha, 0x00a6, + "Firmware load failed for binary blob.\n"); + blob->fw = NULL; + blob = NULL; + goto fw_load_failed; + } + return QLA_SUCCESS; + +fw_load_failed: + return QLA_FUNCTION_FAILED; +} + +int +qla82xx_start_firmware(scsi_qla_host_t *vha) +{ + int pcie_cap; + uint16_t lnk; + struct qla_hw_data *ha = vha->hw; + + /* scrub dma mask expansion register */ + qla82xx_wr_32(ha, CRB_DMA_SHIFT, QLA82XX_DMA_SHIFT_VALUE); + + /* Put both the PEG CMD and RCV PEG to default state + * of 0 before resetting the hardware + */ + qla82xx_wr_32(ha, CRB_CMDPEG_STATE, 0); + qla82xx_wr_32(ha, CRB_RCVPEG_STATE, 0); + + /* Overwrite stale initialization register values */ + qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS1, 0); + qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS2, 0); + + if (qla82xx_load_fw(vha) != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x00a7, + "Error trying to start fw.\n"); + return QLA_FUNCTION_FAILED; + } + + /* Handshake with the card before we register the devices. */ + if (qla82xx_check_cmdpeg_state(ha) != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x00aa, + "Error during card handshake.\n"); + return QLA_FUNCTION_FAILED; + } + + /* Negotiated Link width */ + pcie_cap = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); + pci_read_config_word(ha->pdev, pcie_cap + PCI_EXP_LNKSTA, &lnk); + ha->link_width = (lnk >> 4) & 0x3f; + + /* Synchronize with Receive peg */ + return qla82xx_check_rcvpeg_state(ha); +} + +static uint32_t * +qla82xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, + uint32_t length) +{ + uint32_t i; + uint32_t val; + struct qla_hw_data *ha = vha->hw; + + /* Dword reads to flash. */ + for (i = 0; i < length/4; i++, faddr += 4) { + if (qla82xx_rom_fast_read(ha, faddr, &val)) { + ql_log(ql_log_warn, vha, 0x0106, + "Do ROM fast read failed.\n"); + goto done_read; + } + dwptr[i] = __constant_cpu_to_le32(val); + } +done_read: + return dwptr; +} + +static int +qla82xx_unprotect_flash(struct qla_hw_data *ha) +{ + int ret; + uint32_t val; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + ql_log(ql_log_warn, vha, 0xb014, + "ROM Lock failed.\n"); + return ret; + } + + ret = qla82xx_read_status_reg(ha, &val); + if (ret < 0) + goto done_unprotect; + + val &= ~(BLOCK_PROTECT_BITS << 2); + ret = qla82xx_write_status_reg(ha, val); + if (ret < 0) { + val |= (BLOCK_PROTECT_BITS << 2); + qla82xx_write_status_reg(ha, val); + } + + if (qla82xx_write_disable_flash(ha) != 0) + ql_log(ql_log_warn, vha, 0xb015, + "Write disable failed.\n"); + +done_unprotect: + qla82xx_rom_unlock(ha); + return ret; +} + +static int +qla82xx_protect_flash(struct qla_hw_data *ha) +{ + int ret; + uint32_t val; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + ql_log(ql_log_warn, vha, 0xb016, + "ROM Lock failed.\n"); + return ret; + } + + ret = qla82xx_read_status_reg(ha, &val); + if (ret < 0) + goto done_protect; + + val |= (BLOCK_PROTECT_BITS << 2); + /* LOCK all sectors */ + ret = qla82xx_write_status_reg(ha, val); + if (ret < 0) + ql_log(ql_log_warn, vha, 0xb017, + "Write status register failed.\n"); + + if (qla82xx_write_disable_flash(ha) != 0) + ql_log(ql_log_warn, vha, 0xb018, + "Write disable failed.\n"); +done_protect: + qla82xx_rom_unlock(ha); + return ret; +} + +static int +qla82xx_erase_sector(struct qla_hw_data *ha, int addr) +{ + int ret = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + ql_log(ql_log_warn, vha, 0xb019, + "ROM Lock failed.\n"); + return ret; + } + + qla82xx_flash_set_write_enable(ha); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_SE); + + if (qla82xx_wait_rom_done(ha)) { + ql_log(ql_log_warn, vha, 0xb01a, + "Error waiting for rom done.\n"); + ret = -1; + goto done; + } + ret = qla82xx_flash_wait_write_finish(ha); +done: + qla82xx_rom_unlock(ha); + return ret; +} + +/* + * Address and length are byte address + */ +uint8_t * +qla82xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, + uint32_t offset, uint32_t length) +{ + scsi_block_requests(vha->host); + qla82xx_read_flash_data(vha, (uint32_t *)buf, offset, length); + scsi_unblock_requests(vha->host); + return buf; +} + +static int +qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, + uint32_t faddr, uint32_t dwords) +{ + int ret; + uint32_t liter; + uint32_t sec_mask, rest_addr; + dma_addr_t optrom_dma; + void *optrom = NULL; + int page_mode = 0; + struct qla_hw_data *ha = vha->hw; + + ret = -1; + + /* Prepare burst-capable write on supported ISPs. */ + if (page_mode && !(faddr & 0xfff) && + dwords > OPTROM_BURST_DWORDS) { + optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, + &optrom_dma, GFP_KERNEL); + if (!optrom) { + ql_log(ql_log_warn, vha, 0xb01b, + "Unable to allocate memory " + "for optrom burst write (%x KB).\n", + OPTROM_BURST_SIZE / 1024); + } + } + + rest_addr = ha->fdt_block_size - 1; + sec_mask = ~rest_addr; + + ret = qla82xx_unprotect_flash(ha); + if (ret) { + ql_log(ql_log_warn, vha, 0xb01c, + "Unable to unprotect flash for update.\n"); + goto write_done; + } + + for (liter = 0; liter < dwords; liter++, faddr += 4, dwptr++) { + /* Are we at the beginning of a sector? */ + if ((faddr & rest_addr) == 0) { + + ret = qla82xx_erase_sector(ha, faddr); + if (ret) { + ql_log(ql_log_warn, vha, 0xb01d, + "Unable to erase sector: address=%x.\n", + faddr); + break; + } + } + + /* Go with burst-write. */ + if (optrom && (liter + OPTROM_BURST_DWORDS) <= dwords) { + /* Copy data to DMA'ble buffer. */ + memcpy(optrom, dwptr, OPTROM_BURST_SIZE); + + ret = qla2x00_load_ram(vha, optrom_dma, + (ha->flash_data_off | faddr), + OPTROM_BURST_DWORDS); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xb01e, + "Unable to burst-write optrom segment " + "(%x/%x/%llx).\n", ret, + (ha->flash_data_off | faddr), + (unsigned long long)optrom_dma); + ql_log(ql_log_warn, vha, 0xb01f, + "Reverting to slow-write.\n"); + + dma_free_coherent(&ha->pdev->dev, + OPTROM_BURST_SIZE, optrom, optrom_dma); + optrom = NULL; + } else { + liter += OPTROM_BURST_DWORDS - 1; + faddr += OPTROM_BURST_DWORDS - 1; + dwptr += OPTROM_BURST_DWORDS - 1; + continue; + } + } + + ret = qla82xx_write_flash_dword(ha, faddr, + cpu_to_le32(*dwptr)); + if (ret) { + ql_dbg(ql_dbg_p3p, vha, 0xb020, + "Unable to program flash address=%x data=%x.\n", + faddr, *dwptr); + break; + } + } + + ret = qla82xx_protect_flash(ha); + if (ret) + ql_log(ql_log_warn, vha, 0xb021, + "Unable to protect flash after update.\n"); +write_done: + if (optrom) + dma_free_coherent(&ha->pdev->dev, + OPTROM_BURST_SIZE, optrom, optrom_dma); + return ret; +} + +int +qla82xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, + uint32_t offset, uint32_t length) +{ + int rval; + + /* Suspend HBA. */ + scsi_block_requests(vha->host); + rval = qla82xx_write_flash_data(vha, (uint32_t *)buf, offset, + length >> 2); + scsi_unblock_requests(vha->host); + + /* Convert return ISP82xx to generic */ + if (rval) + rval = QLA_FUNCTION_FAILED; + else + rval = QLA_SUCCESS; + return rval; +} + +void +qla82xx_start_iocbs(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + struct device_reg_82xx __iomem *reg; + uint32_t dbval; + + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + reg = &ha->iobase->isp82; + dbval = 0x04 | (ha->portnum << 5); + + dbval = dbval | (req->id << 8) | (req->ring_index << 16); + if (ql2xdbwr) + qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval); + else { + WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval); + wmb(); + while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, + dbval); + wmb(); + } + } +} + +void qla82xx_rom_lock_recovery(struct qla_hw_data *ha) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + if (qla82xx_rom_lock(ha)) + /* Someone else is holding the lock. */ + ql_log(ql_log_info, vha, 0xb022, + "Resetting rom_lock.\n"); + + /* + * Either we got the lock, or someone + * else died while holding it. + * In either case, unlock. + */ + qla82xx_rom_unlock(ha); +} + +/* + * qla82xx_device_bootstrap + * Initialize device, set DEV_READY, start fw + * + * Note: + * IDC lock must be held upon entry + * + * Return: + * Success : 0 + * Failed : 1 + */ +static int +qla82xx_device_bootstrap(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + int i, timeout; + uint32_t old_count, count; + struct qla_hw_data *ha = vha->hw; + int need_reset = 0, peg_stuck = 1; + + need_reset = qla82xx_need_reset(ha); + + old_count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); + + for (i = 0; i < 10; i++) { + timeout = msleep_interruptible(200); + if (timeout) { + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_FAILED); + return QLA_FUNCTION_FAILED; + } + + count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); + if (count != old_count) + peg_stuck = 0; + } + + if (need_reset) { + /* We are trying to perform a recovery here. */ + if (peg_stuck) + qla82xx_rom_lock_recovery(ha); + goto dev_initialize; + } else { + /* Start of day for this ha context. */ + if (peg_stuck) { + /* Either we are the first or recovery in progress. */ + qla82xx_rom_lock_recovery(ha); + goto dev_initialize; + } else + /* Firmware already running. */ + goto dev_ready; + } + + return rval; + +dev_initialize: + /* set to DEV_INITIALIZING */ + ql_log(ql_log_info, vha, 0x009e, + "HW State: INITIALIZING.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_INITIALIZING); + + /* Driver that sets device state to initializating sets IDC version */ + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION); + + qla82xx_idc_unlock(ha); + rval = qla82xx_start_firmware(vha); + qla82xx_idc_lock(ha); + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x00ad, + "HW State: FAILED.\n"); + qla82xx_clear_drv_active(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_FAILED); + return rval; + } + +dev_ready: + ql_log(ql_log_info, vha, 0x00ae, + "HW State: READY.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_READY); + + return QLA_SUCCESS; +} + +/* +* qla82xx_need_qsnt_handler +* Code to start quiescence sequence +* +* Note: +* IDC lock must be held upon entry +* +* Return: void +*/ + +static void +qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t dev_state, drv_state, drv_active; + unsigned long reset_timeout; + + if (vha->flags.online) { + /*Block any further I/O and wait for pending cmnds to complete*/ + qla82xx_quiescent_state_cleanup(vha); + } + + /* Set the quiescence ready bit */ + qla82xx_set_qsnt_ready(ha); + + /*wait for 30 secs for other functions to ack */ + reset_timeout = jiffies + (30 * HZ); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + /* Its 2 that is written when qsnt is acked, moving one bit */ + drv_active = drv_active << 0x01; + + while (drv_state != drv_active) { + + if (time_after_eq(jiffies, reset_timeout)) { + /* quiescence timeout, other functions didn't ack + * changing the state to DEV_READY + */ + ql_log(ql_log_info, vha, 0xb023, + "%s : QUIESCENT TIMEOUT DRV_ACTIVE:%d " + "DRV_STATE:%d.\n", QLA2XXX_DRIVER_NAME, + drv_active, drv_state); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_READY); + ql_log(ql_log_info, vha, 0xb025, + "HW State: DEV_READY.\n"); + qla82xx_idc_unlock(ha); + qla2x00_perform_loop_resync(vha); + qla82xx_idc_lock(ha); + + qla82xx_clear_qsnt_ready(vha); + return; + } + + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + drv_active = drv_active << 0x01; + } + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + /* everyone acked so set the state to DEV_QUIESCENCE */ + if (dev_state == QLA82XX_DEV_NEED_QUIESCENT) { + ql_log(ql_log_info, vha, 0xb026, + "HW State: DEV_QUIESCENT.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT); + } +} + +/* +* qla82xx_wait_for_state_change +* Wait for device state to change from given current state +* +* Note: +* IDC lock must not be held upon entry +* +* Return: +* Changed device state. +*/ +uint32_t +qla82xx_wait_for_state_change(scsi_qla_host_t *vha, uint32_t curr_state) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t dev_state; + + do { + msleep(1000); + qla82xx_idc_lock(ha); + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + qla82xx_idc_unlock(ha); + } while (dev_state == curr_state); + + return dev_state; +} + +static void +qla82xx_dev_failed_handler(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + /* Disable the board */ + ql_log(ql_log_fatal, vha, 0x00b8, + "Disabling the board.\n"); + + qla82xx_idc_lock(ha); + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + + /* Set DEV_FAILED flag to disable timer */ + vha->device_flags |= DFLG_DEV_FAILED; + qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16); + qla2x00_mark_all_devices_lost(vha, 0); + vha->flags.online = 0; + vha->flags.init_done = 0; +} + +/* + * qla82xx_need_reset_handler + * Code to start reset sequence + * + * Note: + * IDC lock must be held upon entry + * + * Return: + * Success : 0 + * Failed : 1 + */ +static void +qla82xx_need_reset_handler(scsi_qla_host_t *vha) +{ + uint32_t dev_state, drv_state, drv_active; + uint32_t active_mask = 0; + unsigned long reset_timeout; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + if (vha->flags.online) { + qla82xx_idc_unlock(ha); + qla2x00_abort_isp_cleanup(vha); + ha->isp_ops->get_flash_version(vha, req->ring); + ha->isp_ops->nvram_config(vha); + qla82xx_idc_lock(ha); + } + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + if (!ha->flags.isp82xx_reset_owner) { + ql_dbg(ql_dbg_p3p, vha, 0xb028, + "reset_acknowledged by 0x%x\n", ha->portnum); + qla82xx_set_rst_ready(ha); + } else { + active_mask = ~(QLA82XX_DRV_ACTIVE << (ha->portnum * 4)); + drv_active &= active_mask; + ql_dbg(ql_dbg_p3p, vha, 0xb029, + "active_mask: 0x%08x\n", active_mask); + } + + /* wait for 10 seconds for reset ack from all functions */ + reset_timeout = jiffies + (ha->nx_reset_timeout * HZ); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + + ql_dbg(ql_dbg_p3p, vha, 0xb02a, + "drv_state: 0x%08x, drv_active: 0x%08x, " + "dev_state: 0x%08x, active_mask: 0x%08x\n", + drv_state, drv_active, dev_state, active_mask); + + while (drv_state != drv_active && + dev_state != QLA82XX_DEV_INITIALIZING) { + if (time_after_eq(jiffies, reset_timeout)) { + ql_log(ql_log_warn, vha, 0x00b5, + "Reset timeout.\n"); + break; + } + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + if (ha->flags.isp82xx_reset_owner) + drv_active &= active_mask; + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + } + + ql_dbg(ql_dbg_p3p, vha, 0xb02b, + "drv_state: 0x%08x, drv_active: 0x%08x, " + "dev_state: 0x%08x, active_mask: 0x%08x\n", + drv_state, drv_active, dev_state, active_mask); + + ql_log(ql_log_info, vha, 0x00b6, + "Device state is 0x%x = %s.\n", + dev_state, + dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); + + /* Force to DEV_COLD unless someone else is starting a reset */ + if (dev_state != QLA82XX_DEV_INITIALIZING && + dev_state != QLA82XX_DEV_COLD) { + ql_log(ql_log_info, vha, 0x00b7, + "HW State: COLD/RE-INIT.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); + qla82xx_set_rst_ready(ha); + if (ql2xmdenable) { + if (qla82xx_md_collect(vha)) + ql_log(ql_log_warn, vha, 0xb02c, + "Minidump not collected.\n"); + } else + ql_log(ql_log_warn, vha, 0xb04f, + "Minidump disabled.\n"); + } +} + +int +qla82xx_check_md_needed(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint16_t fw_major_version, fw_minor_version, fw_subminor_version; + int rval = QLA_SUCCESS; + + fw_major_version = ha->fw_major_version; + fw_minor_version = ha->fw_minor_version; + fw_subminor_version = ha->fw_subminor_version; + + rval = qla2x00_get_fw_version(vha); + if (rval != QLA_SUCCESS) + return rval; + + if (ql2xmdenable) { + if (!ha->fw_dumped) { + if (fw_major_version != ha->fw_major_version || + fw_minor_version != ha->fw_minor_version || + fw_subminor_version != ha->fw_subminor_version) { + ql_log(ql_log_info, vha, 0xb02d, + "Firmware version differs " + "Previous version: %d:%d:%d - " + "New version: %d:%d:%d\n", + fw_major_version, fw_minor_version, + fw_subminor_version, + ha->fw_major_version, + ha->fw_minor_version, + ha->fw_subminor_version); + /* Release MiniDump resources */ + qla82xx_md_free(vha); + /* ALlocate MiniDump resources */ + qla82xx_md_prep(vha); + } + } else + ql_log(ql_log_info, vha, 0xb02e, + "Firmware dump available to retrieve\n"); + } + return rval; +} + + +int +qla82xx_check_fw_alive(scsi_qla_host_t *vha) +{ + uint32_t fw_heartbeat_counter; + int status = 0; + + fw_heartbeat_counter = qla82xx_rd_32(vha->hw, + QLA82XX_PEG_ALIVE_COUNTER); + /* all 0xff, assume AER/EEH in progress, ignore */ + if (fw_heartbeat_counter == 0xffffffff) { + ql_dbg(ql_dbg_timer, vha, 0x6003, + "FW heartbeat counter is 0xffffffff, " + "returning status=%d.\n", status); + return status; + } + if (vha->fw_heartbeat_counter == fw_heartbeat_counter) { + vha->seconds_since_last_heartbeat++; + /* FW not alive after 2 seconds */ + if (vha->seconds_since_last_heartbeat == 2) { + vha->seconds_since_last_heartbeat = 0; + status = 1; + } + } else + vha->seconds_since_last_heartbeat = 0; + vha->fw_heartbeat_counter = fw_heartbeat_counter; + if (status) + ql_dbg(ql_dbg_timer, vha, 0x6004, + "Returning status=%d.\n", status); + return status; +} + +/* + * qla82xx_device_state_handler + * Main state handler + * + * Note: + * IDC lock must be held upon entry + * + * Return: + * Success : 0 + * Failed : 1 + */ +int +qla82xx_device_state_handler(scsi_qla_host_t *vha) +{ + uint32_t dev_state; + uint32_t old_dev_state; + int rval = QLA_SUCCESS; + unsigned long dev_init_timeout; + struct qla_hw_data *ha = vha->hw; + int loopcount = 0; + + qla82xx_idc_lock(ha); + if (!vha->flags.init_done) + qla82xx_set_drv_active(vha); + + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + old_dev_state = dev_state; + ql_log(ql_log_info, vha, 0x009b, + "Device state is 0x%x = %s.\n", + dev_state, + dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); + + /* wait for 30 seconds for device to go ready */ + dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ); + + while (1) { + + if (time_after_eq(jiffies, dev_init_timeout)) { + ql_log(ql_log_fatal, vha, 0x009c, + "Device init failed.\n"); + rval = QLA_FUNCTION_FAILED; + break; + } + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + if (old_dev_state != dev_state) { + loopcount = 0; + old_dev_state = dev_state; + } + if (loopcount < 5) { + ql_log(ql_log_info, vha, 0x009d, + "Device state is 0x%x = %s.\n", + dev_state, + dev_state < MAX_STATES ? qdev_state(dev_state) : + "Unknown"); + } + switch (dev_state) { + case QLA82XX_DEV_READY: + ha->flags.isp82xx_reset_owner = 0; + goto exit; + case QLA82XX_DEV_COLD: + rval = qla82xx_device_bootstrap(vha); + break; + case QLA82XX_DEV_INITIALIZING: + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + break; + case QLA82XX_DEV_NEED_RESET: + if (!ql2xdontresethba) + qla82xx_need_reset_handler(vha); + else { + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + } + dev_init_timeout = jiffies + + (ha->nx_dev_init_timeout * HZ); + break; + case QLA82XX_DEV_NEED_QUIESCENT: + qla82xx_need_qsnt_handler(vha); + /* Reset timeout value after quiescence handler */ + dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + * HZ); + break; + case QLA82XX_DEV_QUIESCENT: + /* Owner will exit and other will wait for the state + * to get changed + */ + if (ha->flags.quiesce_owner) + goto exit; + + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + + /* Reset timeout value after quiescence handler */ + dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + * HZ); + break; + case QLA82XX_DEV_FAILED: + qla82xx_dev_failed_handler(vha); + rval = QLA_FUNCTION_FAILED; + goto exit; + default: + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + } + loopcount++; + } +exit: + qla82xx_idc_unlock(ha); + return rval; +} + +static int qla82xx_check_temp(scsi_qla_host_t *vha) +{ + uint32_t temp, temp_state, temp_val; + struct qla_hw_data *ha = vha->hw; + + temp = qla82xx_rd_32(ha, CRB_TEMP_STATE); + temp_state = qla82xx_get_temp_state(temp); + temp_val = qla82xx_get_temp_val(temp); + + if (temp_state == QLA82XX_TEMP_PANIC) { + ql_log(ql_log_warn, vha, 0x600e, + "Device temperature %d degrees C exceeds " + " maximum allowed. Hardware has been shut down.\n", + temp_val); + return 1; + } else if (temp_state == QLA82XX_TEMP_WARN) { + ql_log(ql_log_warn, vha, 0x600f, + "Device temperature %d degrees C exceeds " + "operating range. Immediate action needed.\n", + temp_val); + } + return 0; +} + +void qla82xx_clear_pending_mbx(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (ha->flags.mbox_busy) { + ha->flags.mbox_int = 1; + ha->flags.mbox_busy = 0; + ql_log(ql_log_warn, vha, 0x6010, + "Doing premature completion of mbx command.\n"); + if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags)) + complete(&ha->mbx_intr_comp); + } +} + +void +qla82xx_watchdog(scsi_qla_host_t *vha) +{ + uint32_t dev_state, halt_status; + struct qla_hw_data *ha = vha->hw; + + /* don't poll if reset is going on */ + if (!ha->flags.isp82xx_reset_hdlr_active) { + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + if (qla82xx_check_temp(vha)) { + set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); + ha->flags.isp82xx_fw_hung = 1; + qla82xx_clear_pending_mbx(vha); + } else if (dev_state == QLA82XX_DEV_NEED_RESET && + !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) { + ql_log(ql_log_warn, vha, 0x6001, + "Adapter reset needed.\n"); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT && + !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) { + ql_log(ql_log_warn, vha, 0x6002, + "Quiescent needed.\n"); + set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + } else { + if (qla82xx_check_fw_alive(vha)) { + ql_dbg(ql_dbg_timer, vha, 0x6011, + "disabling pause transmit on port 0 & 1.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x98, + CRB_NIU_XG_PAUSE_CTL_P0|CRB_NIU_XG_PAUSE_CTL_P1); + halt_status = qla82xx_rd_32(ha, + QLA82XX_PEG_HALT_STATUS1); + ql_log(ql_log_info, vha, 0x6005, + "dumping hw/fw registers:.\n " + " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,.\n " + " PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,.\n " + " PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,.\n " + " PEG_NET_4_PC: 0x%x.\n", halt_status, + qla82xx_rd_32(ha, QLA82XX_PEG_HALT_STATUS2), + qla82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x3c), + qla82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_1 + 0x3c), + qla82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_2 + 0x3c), + qla82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_3 + 0x3c), + qla82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_4 + 0x3c)); + if (((halt_status & 0x1fffff00) >> 8) == 0x67) + ql_log(ql_log_warn, vha, 0xb052, + "Firmware aborted with " + "error code 0x00006700. Device is " + "being reset.\n"); + if (halt_status & HALT_STATUS_UNRECOVERABLE) { + set_bit(ISP_UNRECOVERABLE, + &vha->dpc_flags); + } else { + ql_log(ql_log_info, vha, 0x6006, + "Detect abort needed.\n"); + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); + } + ha->flags.isp82xx_fw_hung = 1; + ql_log(ql_log_warn, vha, 0x6007, "Firmware hung.\n"); + qla82xx_clear_pending_mbx(vha); + } + } + } +} + +int +qla82xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + rval = qla82xx_device_state_handler(vha); + return rval; +} + +void +qla82xx_set_reset_owner(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t dev_state; + + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + if (dev_state == QLA82XX_DEV_READY) { + ql_log(ql_log_info, vha, 0xb02f, + "HW State: NEED RESET\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_NEED_RESET); + ha->flags.isp82xx_reset_owner = 1; + ql_dbg(ql_dbg_p3p, vha, 0xb030, + "reset_owner is 0x%x\n", ha->portnum); + } else + ql_log(ql_log_info, vha, 0xb031, + "Device state is 0x%x = %s.\n", + dev_state, + dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); +} + +/* + * qla82xx_abort_isp + * Resets ISP and aborts all outstanding commands. + * + * Input: + * ha = adapter block pointer. + * + * Returns: + * 0 = success + */ +int +qla82xx_abort_isp(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_log(ql_log_warn, vha, 0x8024, + "Device in failed state, exiting.\n"); + return QLA_SUCCESS; + } + ha->flags.isp82xx_reset_hdlr_active = 1; + + qla82xx_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla82xx_idc_unlock(ha); + + rval = qla82xx_device_state_handler(vha); + + qla82xx_idc_lock(ha); + qla82xx_clear_rst_ready(ha); + qla82xx_idc_unlock(ha); + + if (rval == QLA_SUCCESS) { + ha->flags.isp82xx_fw_hung = 0; + ha->flags.isp82xx_reset_hdlr_active = 0; + qla82xx_restart_isp(vha); + } + + if (rval) { + vha->flags.online = 1; + if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { + if (ha->isp_abort_cnt == 0) { + ql_log(ql_log_warn, vha, 0x8027, + "ISP error recover failed - board " + "disabled.\n"); + /* + * The next call disables the board + * completely. + */ + ha->isp_ops->reset_adapter(vha); + vha->flags.online = 0; + clear_bit(ISP_ABORT_RETRY, + &vha->dpc_flags); + rval = QLA_SUCCESS; + } else { /* schedule another ISP abort */ + ha->isp_abort_cnt--; + ql_log(ql_log_warn, vha, 0x8036, + "ISP abort - retry remaining %d.\n", + ha->isp_abort_cnt); + rval = QLA_FUNCTION_FAILED; + } + } else { + ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT; + ql_dbg(ql_dbg_taskm, vha, 0x8029, + "ISP error recovery - retrying (%d) more times.\n", + ha->isp_abort_cnt); + set_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + rval = QLA_FUNCTION_FAILED; + } + } + return rval; +} + +/* + * qla82xx_fcoe_ctx_reset + * Perform a quick reset and aborts all outstanding commands. + * This will only perform an FCoE context reset and avoids a full blown + * chip reset. + * + * Input: + * ha = adapter block pointer. + * is_reset_path = flag for identifying the reset path. + * + * Returns: + * 0 = success + */ +int +qla82xx_fcoe_ctx_reset(scsi_qla_host_t *vha) +{ + int rval = QLA_FUNCTION_FAILED; + + if (vha->flags.online) { + /* Abort all outstanding commands, so as to be requeued later */ + qla2x00_abort_isp_cleanup(vha); + } + + /* Stop currently executing firmware. + * This will destroy existing FCoE context at the F/W end. + */ + qla2x00_try_to_stop_firmware(vha); + + /* Restart. Creates a new FCoE context on INIT_FIRMWARE. */ + rval = qla82xx_restart_isp(vha); + + return rval; +} + +/* + * qla2x00_wait_for_fcoe_ctx_reset + * Wait till the FCoE context is reset. + * + * Note: + * Does context switching here. + * Release SPIN_LOCK (if any) before calling this routine. + * + * Return: + * Success (fcoe_ctx reset is done) : 0 + * Failed (fcoe_ctx reset not completed within max loop timout ) : 1 + */ +int +qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *vha) +{ + int status = QLA_FUNCTION_FAILED; + unsigned long wait_reset; + + wait_reset = jiffies + (MAX_LOOP_TIMEOUT * HZ); + while ((test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) + && time_before(jiffies, wait_reset)) { + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + + if (!test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) && + !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { + status = QLA_SUCCESS; + break; + } + } + ql_dbg(ql_dbg_p3p, vha, 0xb027, + "%s: status=%d.\n", __func__, status); + + return status; +} + +void +qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha) +{ + int i; + unsigned long flags; + struct qla_hw_data *ha = vha->hw; + + /* Check if 82XX firmware is alive or not + * We may have arrived here from NEED_RESET + * detection only + */ + if (!ha->flags.isp82xx_fw_hung) { + for (i = 0; i < 2; i++) { + msleep(1000); + if (qla82xx_check_fw_alive(vha)) { + ha->flags.isp82xx_fw_hung = 1; + qla82xx_clear_pending_mbx(vha); + break; + } + } + } + ql_dbg(ql_dbg_init, vha, 0x00b0, + "Entered %s fw_hung=%d.\n", + __func__, ha->flags.isp82xx_fw_hung); + + /* Abort all commands gracefully if fw NOT hung */ + if (!ha->flags.isp82xx_fw_hung) { + int cnt, que; + srb_t *sp; + struct req_que *req; + + spin_lock_irqsave(&ha->hardware_lock, flags); + for (que = 0; que < ha->max_req_queues; que++) { + req = ha->req_q_map[que]; + if (!req) + continue; + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + sp = req->outstanding_cmds[cnt]; + if (sp) { + if (!sp->u.scmd.ctx || + (sp->flags & SRB_FCP_CMND_DMA_VALID)) { + spin_unlock_irqrestore( + &ha->hardware_lock, flags); + if (ha->isp_ops->abort_command(sp)) { + ql_log(ql_log_info, vha, + 0x00b1, + "mbx abort failed.\n"); + } else { + ql_log(ql_log_info, vha, + 0x00b2, + "mbx abort success.\n"); + } + spin_lock_irqsave(&ha->hardware_lock, flags); + } + } + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Wait for pending cmds (physical and virtual) to complete */ + if (!qla2x00_eh_wait_for_pending_commands(vha, 0, 0, + WAIT_HOST) == QLA_SUCCESS) { + ql_dbg(ql_dbg_init, vha, 0x00b3, + "Done wait for " + "pending commands.\n"); + } + } +} + + +/* Minidump related functions */ +static int +qla82xx_minidump_process_control(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + struct qla82xx_md_entry_crb *crb_entry; + uint32_t read_value, opcode, poll_time; + uint32_t addr, index, crb_addr; + unsigned long wtime; + struct qla82xx_md_template_hdr *tmplt_hdr; + uint32_t rval = QLA_SUCCESS; + int i; + + tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr; + crb_entry = (struct qla82xx_md_entry_crb *)entry_hdr; + crb_addr = crb_entry->addr; + + for (i = 0; i < crb_entry->op_count; i++) { + opcode = crb_entry->crb_ctrl.opcode; + if (opcode & QLA82XX_DBG_OPCODE_WR) { + qla82xx_md_rw_32(ha, crb_addr, + crb_entry->value_1, 1); + opcode &= ~QLA82XX_DBG_OPCODE_WR; + } + + if (opcode & QLA82XX_DBG_OPCODE_RW) { + read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); + qla82xx_md_rw_32(ha, crb_addr, read_value, 1); + opcode &= ~QLA82XX_DBG_OPCODE_RW; + } + + if (opcode & QLA82XX_DBG_OPCODE_AND) { + read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); + read_value &= crb_entry->value_2; + opcode &= ~QLA82XX_DBG_OPCODE_AND; + if (opcode & QLA82XX_DBG_OPCODE_OR) { + read_value |= crb_entry->value_3; + opcode &= ~QLA82XX_DBG_OPCODE_OR; + } + qla82xx_md_rw_32(ha, crb_addr, read_value, 1); + } + + if (opcode & QLA82XX_DBG_OPCODE_OR) { + read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); + read_value |= crb_entry->value_3; + qla82xx_md_rw_32(ha, crb_addr, read_value, 1); + opcode &= ~QLA82XX_DBG_OPCODE_OR; + } + + if (opcode & QLA82XX_DBG_OPCODE_POLL) { + poll_time = crb_entry->crb_strd.poll_timeout; + wtime = jiffies + poll_time; + read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); + + do { + if ((read_value & crb_entry->value_2) + == crb_entry->value_1) + break; + else if (time_after_eq(jiffies, wtime)) { + /* capturing dump failed */ + rval = QLA_FUNCTION_FAILED; + break; + } else + read_value = qla82xx_md_rw_32(ha, + crb_addr, 0, 0); + } while (1); + opcode &= ~QLA82XX_DBG_OPCODE_POLL; + } + + if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) { + if (crb_entry->crb_strd.state_index_a) { + index = crb_entry->crb_strd.state_index_a; + addr = tmplt_hdr->saved_state_array[index]; + } else + addr = crb_addr; + + read_value = qla82xx_md_rw_32(ha, addr, 0, 0); + index = crb_entry->crb_ctrl.state_index_v; + tmplt_hdr->saved_state_array[index] = read_value; + opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE; + } + + if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) { + if (crb_entry->crb_strd.state_index_a) { + index = crb_entry->crb_strd.state_index_a; + addr = tmplt_hdr->saved_state_array[index]; + } else + addr = crb_addr; + + if (crb_entry->crb_ctrl.state_index_v) { + index = crb_entry->crb_ctrl.state_index_v; + read_value = + tmplt_hdr->saved_state_array[index]; + } else + read_value = crb_entry->value_1; + + qla82xx_md_rw_32(ha, addr, read_value, 1); + opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE; + } + + if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) { + index = crb_entry->crb_ctrl.state_index_v; + read_value = tmplt_hdr->saved_state_array[index]; + read_value <<= crb_entry->crb_ctrl.shl; + read_value >>= crb_entry->crb_ctrl.shr; + if (crb_entry->value_2) + read_value &= crb_entry->value_2; + read_value |= crb_entry->value_3; + read_value += crb_entry->value_1; + tmplt_hdr->saved_state_array[index] = read_value; + opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE; + } + crb_addr += crb_entry->crb_strd.addr_stride; + } + return rval; +} + +static void +qla82xx_minidump_process_rdocm(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t r_addr, r_stride, loop_cnt, i, r_value; + struct qla82xx_md_entry_rdocm *ocm_hdr; + uint32_t *data_ptr = *d_ptr; + + ocm_hdr = (struct qla82xx_md_entry_rdocm *)entry_hdr; + r_addr = ocm_hdr->read_addr; + r_stride = ocm_hdr->read_addr_stride; + loop_cnt = ocm_hdr->op_count; + + for (i = 0; i < loop_cnt; i++) { + r_value = RD_REG_DWORD((void *)(r_addr + ha->nx_pcibase)); + *data_ptr++ = cpu_to_le32(r_value); + r_addr += r_stride; + } + *d_ptr = data_ptr; +} + +static void +qla82xx_minidump_process_rdmux(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value; + struct qla82xx_md_entry_mux *mux_hdr; + uint32_t *data_ptr = *d_ptr; + + mux_hdr = (struct qla82xx_md_entry_mux *)entry_hdr; + r_addr = mux_hdr->read_addr; + s_addr = mux_hdr->select_addr; + s_stride = mux_hdr->select_value_stride; + s_value = mux_hdr->select_value; + loop_cnt = mux_hdr->op_count; + + for (i = 0; i < loop_cnt; i++) { + qla82xx_md_rw_32(ha, s_addr, s_value, 1); + r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0); + *data_ptr++ = cpu_to_le32(s_value); + *data_ptr++ = cpu_to_le32(r_value); + s_value += s_stride; + } + *d_ptr = data_ptr; +} + +static void +qla82xx_minidump_process_rdcrb(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t r_addr, r_stride, loop_cnt, i, r_value; + struct qla82xx_md_entry_crb *crb_hdr; + uint32_t *data_ptr = *d_ptr; + + crb_hdr = (struct qla82xx_md_entry_crb *)entry_hdr; + r_addr = crb_hdr->addr; + r_stride = crb_hdr->crb_strd.addr_stride; + loop_cnt = crb_hdr->op_count; + + for (i = 0; i < loop_cnt; i++) { + r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0); + *data_ptr++ = cpu_to_le32(r_addr); + *data_ptr++ = cpu_to_le32(r_value); + r_addr += r_stride; + } + *d_ptr = data_ptr; +} + +static int +qla82xx_minidump_process_l2tag(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t addr, r_addr, c_addr, t_r_addr; + uint32_t i, k, loop_count, t_value, r_cnt, r_value; + unsigned long p_wait, w_time, p_mask; + uint32_t c_value_w, c_value_r; + struct qla82xx_md_entry_cache *cache_hdr; + int rval = QLA_FUNCTION_FAILED; + uint32_t *data_ptr = *d_ptr; + + cache_hdr = (struct qla82xx_md_entry_cache *)entry_hdr; + loop_count = cache_hdr->op_count; + r_addr = cache_hdr->read_addr; + c_addr = cache_hdr->control_addr; + c_value_w = cache_hdr->cache_ctrl.write_value; + + t_r_addr = cache_hdr->tag_reg_addr; + t_value = cache_hdr->addr_ctrl.init_tag_value; + r_cnt = cache_hdr->read_ctrl.read_addr_cnt; + p_wait = cache_hdr->cache_ctrl.poll_wait; + p_mask = cache_hdr->cache_ctrl.poll_mask; + + for (i = 0; i < loop_count; i++) { + qla82xx_md_rw_32(ha, t_r_addr, t_value, 1); + if (c_value_w) + qla82xx_md_rw_32(ha, c_addr, c_value_w, 1); + + if (p_mask) { + w_time = jiffies + p_wait; + do { + c_value_r = qla82xx_md_rw_32(ha, c_addr, 0, 0); + if ((c_value_r & p_mask) == 0) + break; + else if (time_after_eq(jiffies, w_time)) { + /* capturing dump failed */ + ql_dbg(ql_dbg_p3p, vha, 0xb032, + "c_value_r: 0x%x, poll_mask: 0x%lx," + " w_time: 0x%lx\n", + c_value_r, p_mask, w_time); + return rval; + } + } while (1); + } + + addr = r_addr; + for (k = 0; k < r_cnt; k++) { + r_value = qla82xx_md_rw_32(ha, addr, 0, 0); + *data_ptr++ = cpu_to_le32(r_value); + addr += cache_hdr->read_ctrl.read_addr_stride; + } + t_value += cache_hdr->addr_ctrl.tag_value_stride; + } + *d_ptr = data_ptr; + return QLA_SUCCESS; +} + +static void +qla82xx_minidump_process_l1cache(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t addr, r_addr, c_addr, t_r_addr; + uint32_t i, k, loop_count, t_value, r_cnt, r_value; + uint32_t c_value_w; + struct qla82xx_md_entry_cache *cache_hdr; + uint32_t *data_ptr = *d_ptr; + + cache_hdr = (struct qla82xx_md_entry_cache *)entry_hdr; + loop_count = cache_hdr->op_count; + r_addr = cache_hdr->read_addr; + c_addr = cache_hdr->control_addr; + c_value_w = cache_hdr->cache_ctrl.write_value; + + t_r_addr = cache_hdr->tag_reg_addr; + t_value = cache_hdr->addr_ctrl.init_tag_value; + r_cnt = cache_hdr->read_ctrl.read_addr_cnt; + + for (i = 0; i < loop_count; i++) { + qla82xx_md_rw_32(ha, t_r_addr, t_value, 1); + qla82xx_md_rw_32(ha, c_addr, c_value_w, 1); + addr = r_addr; + for (k = 0; k < r_cnt; k++) { + r_value = qla82xx_md_rw_32(ha, addr, 0, 0); + *data_ptr++ = cpu_to_le32(r_value); + addr += cache_hdr->read_ctrl.read_addr_stride; + } + t_value += cache_hdr->addr_ctrl.tag_value_stride; + } + *d_ptr = data_ptr; +} + +static void +qla82xx_minidump_process_queue(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t s_addr, r_addr; + uint32_t r_stride, r_value, r_cnt, qid = 0; + uint32_t i, k, loop_cnt; + struct qla82xx_md_entry_queue *q_hdr; + uint32_t *data_ptr = *d_ptr; + + q_hdr = (struct qla82xx_md_entry_queue *)entry_hdr; + s_addr = q_hdr->select_addr; + r_cnt = q_hdr->rd_strd.read_addr_cnt; + r_stride = q_hdr->rd_strd.read_addr_stride; + loop_cnt = q_hdr->op_count; + + for (i = 0; i < loop_cnt; i++) { + qla82xx_md_rw_32(ha, s_addr, qid, 1); + r_addr = q_hdr->read_addr; + for (k = 0; k < r_cnt; k++) { + r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0); + *data_ptr++ = cpu_to_le32(r_value); + r_addr += r_stride; + } + qid += q_hdr->q_strd.queue_id_stride; + } + *d_ptr = data_ptr; +} + +static void +qla82xx_minidump_process_rdrom(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t r_addr, r_value; + uint32_t i, loop_cnt; + struct qla82xx_md_entry_rdrom *rom_hdr; + uint32_t *data_ptr = *d_ptr; + + rom_hdr = (struct qla82xx_md_entry_rdrom *)entry_hdr; + r_addr = rom_hdr->read_addr; + loop_cnt = rom_hdr->read_data_size/sizeof(uint32_t); + + for (i = 0; i < loop_cnt; i++) { + qla82xx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW, + (r_addr & 0xFFFF0000), 1); + r_value = qla82xx_md_rw_32(ha, + MD_DIRECT_ROM_READ_BASE + + (r_addr & 0x0000FFFF), 0, 0); + *data_ptr++ = cpu_to_le32(r_value); + r_addr += sizeof(uint32_t); + } + *d_ptr = data_ptr; +} + +static int +qla82xx_minidump_process_rdmem(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t r_addr, r_value, r_data; + uint32_t i, j, loop_cnt; + struct qla82xx_md_entry_rdmem *m_hdr; + unsigned long flags; + int rval = QLA_FUNCTION_FAILED; + uint32_t *data_ptr = *d_ptr; + + m_hdr = (struct qla82xx_md_entry_rdmem *)entry_hdr; + r_addr = m_hdr->read_addr; + loop_cnt = m_hdr->read_data_size/16; + + if (r_addr & 0xf) { + ql_log(ql_log_warn, vha, 0xb033, + "Read addr 0x%x not 16 bytes alligned\n", r_addr); + return rval; + } + + if (m_hdr->read_data_size % 16) { + ql_log(ql_log_warn, vha, 0xb034, + "Read data[0x%x] not multiple of 16 bytes\n", + m_hdr->read_data_size); + return rval; + } + + ql_dbg(ql_dbg_p3p, vha, 0xb035, + "[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n", + __func__, r_addr, m_hdr->read_data_size, loop_cnt); + + write_lock_irqsave(&ha->hw_lock, flags); + for (i = 0; i < loop_cnt; i++) { + qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_LO, r_addr, 1); + r_value = 0; + qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_HI, r_value, 1); + r_value = MIU_TA_CTL_ENABLE; + qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1); + r_value = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE; + qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + r_value = qla82xx_md_rw_32(ha, + MD_MIU_TEST_AGT_CTRL, 0, 0); + if ((r_value & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&ha->pdev->dev, + "failed to read through agent\n"); + write_unlock_irqrestore(&ha->hw_lock, flags); + return rval; + } + + for (j = 0; j < 4; j++) { + r_data = qla82xx_md_rw_32(ha, + MD_MIU_TEST_AGT_RDDATA[j], 0, 0); + *data_ptr++ = cpu_to_le32(r_data); + } + r_addr += 16; + } + write_unlock_irqrestore(&ha->hw_lock, flags); + *d_ptr = data_ptr; + return QLA_SUCCESS; +} + +static int +qla82xx_validate_template_chksum(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint64_t chksum = 0; + uint32_t *d_ptr = (uint32_t *)ha->md_tmplt_hdr; + int count = ha->md_template_size/sizeof(uint32_t); + + while (count-- > 0) + chksum += *d_ptr++; + while (chksum >> 32) + chksum = (chksum & 0xFFFFFFFF) + (chksum >> 32); + return ~chksum; +} + +static void +qla82xx_mark_entry_skipped(scsi_qla_host_t *vha, + qla82xx_md_entry_hdr_t *entry_hdr, int index) +{ + + entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG; + ql_dbg(ql_dbg_p3p, vha, 0xb036, + "Skipping entry[%d]: " + "ETYPE[0x%x]-ELEVEL[0x%x]\n", + index, entry_hdr->entry_type, + entry_hdr->d_ctrl.entry_capture_mask); +} + +int +qla82xx_md_collect(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + int no_entry_hdr = 0; + qla82xx_md_entry_hdr_t *entry_hdr; + struct qla82xx_md_template_hdr *tmplt_hdr; + uint32_t *data_ptr; + uint32_t total_data_size = 0, f_capture_mask, data_collected = 0; + int i = 0, rval = QLA_FUNCTION_FAILED; + + tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr; + data_ptr = (uint32_t *)ha->md_dump; + + if (ha->fw_dumped) { + ql_log(ql_log_warn, vha, 0xb037, + "Firmware has been previously dumped (%p) " + "-- ignoring request.\n", ha->fw_dump); + goto md_failed; + } + + ha->fw_dumped = 0; + + if (!ha->md_tmplt_hdr || !ha->md_dump) { + ql_log(ql_log_warn, vha, 0xb038, + "Memory not allocated for minidump capture\n"); + goto md_failed; + } + + if (ha->flags.isp82xx_no_md_cap) { + ql_log(ql_log_warn, vha, 0xb054, + "Forced reset from application, " + "ignore minidump capture\n"); + ha->flags.isp82xx_no_md_cap = 0; + goto md_failed; + } + + if (qla82xx_validate_template_chksum(vha)) { + ql_log(ql_log_info, vha, 0xb039, + "Template checksum validation error\n"); + goto md_failed; + } + + no_entry_hdr = tmplt_hdr->num_of_entries; + ql_dbg(ql_dbg_p3p, vha, 0xb03a, + "No of entry headers in Template: 0x%x\n", no_entry_hdr); + + ql_dbg(ql_dbg_p3p, vha, 0xb03b, + "Capture Mask obtained: 0x%x\n", tmplt_hdr->capture_debug_level); + + f_capture_mask = tmplt_hdr->capture_debug_level & 0xFF; + + /* Validate whether required debug level is set */ + if ((f_capture_mask & 0x3) != 0x3) { + ql_log(ql_log_warn, vha, 0xb03c, + "Minimum required capture mask[0x%x] level not set\n", + f_capture_mask); + goto md_failed; + } + tmplt_hdr->driver_capture_mask = ql2xmdcapmask; + + tmplt_hdr->driver_info[0] = vha->host_no; + tmplt_hdr->driver_info[1] = (QLA_DRIVER_MAJOR_VER << 24) | + (QLA_DRIVER_MINOR_VER << 16) | (QLA_DRIVER_PATCH_VER << 8) | + QLA_DRIVER_BETA_VER; + + total_data_size = ha->md_dump_size; + + ql_dbg(ql_dbg_p3p, vha, 0xb03d, + "Total minidump data_size 0x%x to be captured\n", total_data_size); + + /* Check whether template obtained is valid */ + if (tmplt_hdr->entry_type != QLA82XX_TLHDR) { + ql_log(ql_log_warn, vha, 0xb04e, + "Bad template header entry type: 0x%x obtained\n", + tmplt_hdr->entry_type); + goto md_failed; + } + + entry_hdr = (qla82xx_md_entry_hdr_t *) \ + (((uint8_t *)ha->md_tmplt_hdr) + tmplt_hdr->first_entry_offset); + + /* Walk through the entry headers */ + for (i = 0; i < no_entry_hdr; i++) { + + if (data_collected > total_data_size) { + ql_log(ql_log_warn, vha, 0xb03e, + "More MiniDump data collected: [0x%x]\n", + data_collected); + goto md_failed; + } + + if (!(entry_hdr->d_ctrl.entry_capture_mask & + ql2xmdcapmask)) { + entry_hdr->d_ctrl.driver_flags |= + QLA82XX_DBG_SKIPPED_FLAG; + ql_dbg(ql_dbg_p3p, vha, 0xb03f, + "Skipping entry[%d]: " + "ETYPE[0x%x]-ELEVEL[0x%x]\n", + i, entry_hdr->entry_type, + entry_hdr->d_ctrl.entry_capture_mask); + goto skip_nxt_entry; + } + + ql_dbg(ql_dbg_p3p, vha, 0xb040, + "[%s]: data ptr[%d]: %p, entry_hdr: %p\n" + "entry_type: 0x%x, captrue_mask: 0x%x\n", + __func__, i, data_ptr, entry_hdr, + entry_hdr->entry_type, + entry_hdr->d_ctrl.entry_capture_mask); + + ql_dbg(ql_dbg_p3p, vha, 0xb041, + "Data collected: [0x%x], Dump size left:[0x%x]\n", + data_collected, (ha->md_dump_size - data_collected)); + + /* Decode the entry type and take + * required action to capture debug data */ + switch (entry_hdr->entry_type) { + case QLA82XX_RDEND: + qla82xx_mark_entry_skipped(vha, entry_hdr, i); + break; + case QLA82XX_CNTRL: + rval = qla82xx_minidump_process_control(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) { + qla82xx_mark_entry_skipped(vha, entry_hdr, i); + goto md_failed; + } + break; + case QLA82XX_RDCRB: + qla82xx_minidump_process_rdcrb(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_RDMEM: + rval = qla82xx_minidump_process_rdmem(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) { + qla82xx_mark_entry_skipped(vha, entry_hdr, i); + goto md_failed; + } + break; + case QLA82XX_BOARD: + case QLA82XX_RDROM: + qla82xx_minidump_process_rdrom(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_L2DTG: + case QLA82XX_L2ITG: + case QLA82XX_L2DAT: + case QLA82XX_L2INS: + rval = qla82xx_minidump_process_l2tag(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) { + qla82xx_mark_entry_skipped(vha, entry_hdr, i); + goto md_failed; + } + break; + case QLA82XX_L1DAT: + case QLA82XX_L1INS: + qla82xx_minidump_process_l1cache(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_RDOCM: + qla82xx_minidump_process_rdocm(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_RDMUX: + qla82xx_minidump_process_rdmux(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_QUEUE: + qla82xx_minidump_process_queue(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_RDNOP: + default: + qla82xx_mark_entry_skipped(vha, entry_hdr, i); + break; + } + + ql_dbg(ql_dbg_p3p, vha, 0xb042, + "[%s]: data ptr[%d]: %p\n", __func__, i, data_ptr); + + data_collected = (uint8_t *)data_ptr - + (uint8_t *)ha->md_dump; +skip_nxt_entry: + entry_hdr = (qla82xx_md_entry_hdr_t *) \ + (((uint8_t *)entry_hdr) + entry_hdr->entry_size); + } + + if (data_collected != total_data_size) { + ql_dbg(ql_dbg_p3p, vha, 0xb043, + "MiniDump data mismatch: Data collected: [0x%x]," + "total_data_size:[0x%x]\n", + data_collected, total_data_size); + goto md_failed; + } + + ql_log(ql_log_info, vha, 0xb044, + "Firmware dump saved to temp buffer (%ld/%p %ld/%p).\n", + vha->host_no, ha->md_tmplt_hdr, vha->host_no, ha->md_dump); + ha->fw_dumped = 1; + qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP); + +md_failed: + return rval; +} + +int +qla82xx_md_alloc(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + int i, k; + struct qla82xx_md_template_hdr *tmplt_hdr; + + tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr; + + if (ql2xmdcapmask < 0x3 || ql2xmdcapmask > 0x7F) { + ql2xmdcapmask = tmplt_hdr->capture_debug_level & 0xFF; + ql_log(ql_log_info, vha, 0xb045, + "Forcing driver capture mask to firmware default capture mask: 0x%x.\n", + ql2xmdcapmask); + } + + for (i = 0x2, k = 1; (i & QLA82XX_DEFAULT_CAP_MASK); i <<= 1, k++) { + if (i & ql2xmdcapmask) + ha->md_dump_size += tmplt_hdr->capture_size_array[k]; + } + + if (ha->md_dump) { + ql_log(ql_log_warn, vha, 0xb046, + "Firmware dump previously allocated.\n"); + return 1; + } + + ha->md_dump = vmalloc(ha->md_dump_size); + if (ha->md_dump == NULL) { + ql_log(ql_log_warn, vha, 0xb047, + "Unable to allocate memory for Minidump size " + "(0x%x).\n", ha->md_dump_size); + return 1; + } + return 0; +} + +void +qla82xx_md_free(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + /* Release the template header allocated */ + if (ha->md_tmplt_hdr) { + ql_log(ql_log_info, vha, 0xb048, + "Free MiniDump template: %p, size (%d KB)\n", + ha->md_tmplt_hdr, ha->md_template_size / 1024); + dma_free_coherent(&ha->pdev->dev, ha->md_template_size, + ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma); + ha->md_tmplt_hdr = 0; + } + + /* Release the template data buffer allocated */ + if (ha->md_dump) { + ql_log(ql_log_info, vha, 0xb049, + "Free MiniDump memory: %p, size (%d KB)\n", + ha->md_dump, ha->md_dump_size / 1024); + vfree(ha->md_dump); + ha->md_dump_size = 0; + ha->md_dump = 0; + } +} + +void +qla82xx_md_prep(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + int rval; + + /* Get Minidump template size */ + rval = qla82xx_md_get_template_size(vha); + if (rval == QLA_SUCCESS) { + ql_log(ql_log_info, vha, 0xb04a, + "MiniDump Template size obtained (%d KB)\n", + ha->md_template_size / 1024); + + /* Get Minidump template */ + rval = qla82xx_md_get_template(vha); + if (rval == QLA_SUCCESS) { + ql_dbg(ql_dbg_p3p, vha, 0xb04b, + "MiniDump Template obtained\n"); + + /* Allocate memory for minidump */ + rval = qla82xx_md_alloc(vha); + if (rval == QLA_SUCCESS) + ql_log(ql_log_info, vha, 0xb04c, + "MiniDump memory allocated (%d KB)\n", + ha->md_dump_size / 1024); + else { + ql_log(ql_log_info, vha, 0xb04d, + "Free MiniDump template: %p, size: (%d KB)\n", + ha->md_tmplt_hdr, + ha->md_template_size / 1024); + dma_free_coherent(&ha->pdev->dev, + ha->md_template_size, + ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma); + ha->md_tmplt_hdr = 0; + } + + } + } +} + +int +qla82xx_beacon_on(struct scsi_qla_host *vha) +{ + + int rval; + struct qla_hw_data *ha = vha->hw; + qla82xx_idc_lock(ha); + rval = qla82xx_mbx_beacon_ctl(vha, 1); + + if (rval) + { + ql_log(ql_log_warn, vha, 0xb050, + "mbx set led config failed in %s\n", __func__); + goto exit; + } + ha->beacon_blink_led = 1; +exit: + qla82xx_idc_unlock(ha); + return rval; +} + +int +qla82xx_beacon_off(struct scsi_qla_host *vha) +{ + + int rval; + struct qla_hw_data *ha = vha->hw; + qla82xx_idc_lock(ha); + rval = qla82xx_mbx_beacon_ctl(vha, 0); + + if (rval) + { + ql_log(ql_log_warn, vha, 0xb051, + "mbx set led config failed in %s\n", __func__); + goto exit; + } + ha->beacon_blink_led = 0; +exit: + qla82xx_idc_unlock(ha); + return rval; +} diff --git a/qla2x00t/qla_nx.h b/qla2x00t/qla_nx.h new file mode 100644 index 000000000..d0d34e14b --- /dev/null +++ b/qla2x00t/qla_nx.h @@ -0,0 +1,1194 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2011 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#ifndef __QLA_NX_H +#define __QLA_NX_H + +/* + * Following are the states of the Phantom. Phantom will set them and + * Host will read to check if the fields are correct. +*/ +#define PHAN_INITIALIZE_FAILED 0xffff +#define PHAN_INITIALIZE_COMPLETE 0xff01 + +/* Host writes the following to notify that it has done the init-handshake */ +#define PHAN_INITIALIZE_ACK 0xf00f +#define PHAN_PEG_RCV_INITIALIZED 0xff01 + +/*CRB_RELATED*/ +#define QLA82XX_CRB_BASE QLA82XX_CAM_RAM(0x200) +#define QLA82XX_REG(X) (QLA82XX_CRB_BASE+(X)) + +#define CRB_CMDPEG_STATE QLA82XX_REG(0x50) +#define CRB_RCVPEG_STATE QLA82XX_REG(0x13c) +#define BOOT_LOADER_DIMM_STATUS QLA82XX_REG(0x54) +#define CRB_DMA_SHIFT QLA82XX_REG(0xcc) +#define CRB_TEMP_STATE QLA82XX_REG(0x1b4) +#define QLA82XX_DMA_SHIFT_VALUE 0x55555555 + +#define QLA82XX_HW_H0_CH_HUB_ADR 0x05 +#define QLA82XX_HW_H1_CH_HUB_ADR 0x0E +#define QLA82XX_HW_H2_CH_HUB_ADR 0x03 +#define QLA82XX_HW_H3_CH_HUB_ADR 0x01 +#define QLA82XX_HW_H4_CH_HUB_ADR 0x06 +#define QLA82XX_HW_H5_CH_HUB_ADR 0x07 +#define QLA82XX_HW_H6_CH_HUB_ADR 0x08 + +/* Hub 0 */ +#define QLA82XX_HW_MN_CRB_AGT_ADR 0x15 +#define QLA82XX_HW_MS_CRB_AGT_ADR 0x25 + +/* Hub 1 */ +#define QLA82XX_HW_PS_CRB_AGT_ADR 0x73 +#define QLA82XX_HW_QMS_CRB_AGT_ADR 0x00 +#define QLA82XX_HW_RPMX3_CRB_AGT_ADR 0x0b +#define QLA82XX_HW_SQGS0_CRB_AGT_ADR 0x01 +#define QLA82XX_HW_SQGS1_CRB_AGT_ADR 0x02 +#define QLA82XX_HW_SQGS2_CRB_AGT_ADR 0x03 +#define QLA82XX_HW_SQGS3_CRB_AGT_ADR 0x04 +#define QLA82XX_HW_C2C0_CRB_AGT_ADR 0x58 +#define QLA82XX_HW_C2C1_CRB_AGT_ADR 0x59 +#define QLA82XX_HW_C2C2_CRB_AGT_ADR 0x5a +#define QLA82XX_HW_RPMX2_CRB_AGT_ADR 0x0a +#define QLA82XX_HW_RPMX4_CRB_AGT_ADR 0x0c +#define QLA82XX_HW_RPMX7_CRB_AGT_ADR 0x0f +#define QLA82XX_HW_RPMX9_CRB_AGT_ADR 0x12 +#define QLA82XX_HW_SMB_CRB_AGT_ADR 0x18 + +/* Hub 2 */ +#define QLA82XX_HW_NIU_CRB_AGT_ADR 0x31 +#define QLA82XX_HW_I2C0_CRB_AGT_ADR 0x19 +#define QLA82XX_HW_I2C1_CRB_AGT_ADR 0x29 + +#define QLA82XX_HW_SN_CRB_AGT_ADR 0x10 +#define QLA82XX_HW_I2Q_CRB_AGT_ADR 0x20 +#define QLA82XX_HW_LPC_CRB_AGT_ADR 0x22 +#define QLA82XX_HW_ROMUSB_CRB_AGT_ADR 0x21 +#define QLA82XX_HW_QM_CRB_AGT_ADR 0x66 +#define QLA82XX_HW_SQG0_CRB_AGT_ADR 0x60 +#define QLA82XX_HW_SQG1_CRB_AGT_ADR 0x61 +#define QLA82XX_HW_SQG2_CRB_AGT_ADR 0x62 +#define QLA82XX_HW_SQG3_CRB_AGT_ADR 0x63 +#define QLA82XX_HW_RPMX1_CRB_AGT_ADR 0x09 +#define QLA82XX_HW_RPMX5_CRB_AGT_ADR 0x0d +#define QLA82XX_HW_RPMX6_CRB_AGT_ADR 0x0e +#define QLA82XX_HW_RPMX8_CRB_AGT_ADR 0x11 + +/* Hub 3 */ +#define QLA82XX_HW_PH_CRB_AGT_ADR 0x1A +#define QLA82XX_HW_SRE_CRB_AGT_ADR 0x50 +#define QLA82XX_HW_EG_CRB_AGT_ADR 0x51 +#define QLA82XX_HW_RPMX0_CRB_AGT_ADR 0x08 + +/* Hub 4 */ +#define QLA82XX_HW_PEGN0_CRB_AGT_ADR 0x40 +#define QLA82XX_HW_PEGN1_CRB_AGT_ADR 0x41 +#define QLA82XX_HW_PEGN2_CRB_AGT_ADR 0x42 +#define QLA82XX_HW_PEGN3_CRB_AGT_ADR 0x43 +#define QLA82XX_HW_PEGNI_CRB_AGT_ADR 0x44 +#define QLA82XX_HW_PEGND_CRB_AGT_ADR 0x45 +#define QLA82XX_HW_PEGNC_CRB_AGT_ADR 0x46 +#define QLA82XX_HW_PEGR0_CRB_AGT_ADR 0x47 +#define QLA82XX_HW_PEGR1_CRB_AGT_ADR 0x48 +#define QLA82XX_HW_PEGR2_CRB_AGT_ADR 0x49 +#define QLA82XX_HW_PEGR3_CRB_AGT_ADR 0x4a +#define QLA82XX_HW_PEGN4_CRB_AGT_ADR 0x4b + +/* Hub 5 */ +#define QLA82XX_HW_PEGS0_CRB_AGT_ADR 0x40 +#define QLA82XX_HW_PEGS1_CRB_AGT_ADR 0x41 +#define QLA82XX_HW_PEGS2_CRB_AGT_ADR 0x42 +#define QLA82XX_HW_PEGS3_CRB_AGT_ADR 0x43 +#define QLA82XX_HW_PEGSI_CRB_AGT_ADR 0x44 +#define QLA82XX_HW_PEGSD_CRB_AGT_ADR 0x45 +#define QLA82XX_HW_PEGSC_CRB_AGT_ADR 0x46 + +/* Hub 6 */ +#define QLA82XX_HW_CAS0_CRB_AGT_ADR 0x46 +#define QLA82XX_HW_CAS1_CRB_AGT_ADR 0x47 +#define QLA82XX_HW_CAS2_CRB_AGT_ADR 0x48 +#define QLA82XX_HW_CAS3_CRB_AGT_ADR 0x49 +#define QLA82XX_HW_NCM_CRB_AGT_ADR 0x16 +#define QLA82XX_HW_TMR_CRB_AGT_ADR 0x17 +#define QLA82XX_HW_XDMA_CRB_AGT_ADR 0x05 +#define QLA82XX_HW_OCM0_CRB_AGT_ADR 0x06 +#define QLA82XX_HW_OCM1_CRB_AGT_ADR 0x07 + +/* This field defines PCI/X adr [25:20] of agents on the CRB */ +/* */ +#define QLA82XX_HW_PX_MAP_CRB_PH 0 +#define QLA82XX_HW_PX_MAP_CRB_PS 1 +#define QLA82XX_HW_PX_MAP_CRB_MN 2 +#define QLA82XX_HW_PX_MAP_CRB_MS 3 +#define QLA82XX_HW_PX_MAP_CRB_SRE 5 +#define QLA82XX_HW_PX_MAP_CRB_NIU 6 +#define QLA82XX_HW_PX_MAP_CRB_QMN 7 +#define QLA82XX_HW_PX_MAP_CRB_SQN0 8 +#define QLA82XX_HW_PX_MAP_CRB_SQN1 9 +#define QLA82XX_HW_PX_MAP_CRB_SQN2 10 +#define QLA82XX_HW_PX_MAP_CRB_SQN3 11 +#define QLA82XX_HW_PX_MAP_CRB_QMS 12 +#define QLA82XX_HW_PX_MAP_CRB_SQS0 13 +#define QLA82XX_HW_PX_MAP_CRB_SQS1 14 +#define QLA82XX_HW_PX_MAP_CRB_SQS2 15 +#define QLA82XX_HW_PX_MAP_CRB_SQS3 16 +#define QLA82XX_HW_PX_MAP_CRB_PGN0 17 +#define QLA82XX_HW_PX_MAP_CRB_PGN1 18 +#define QLA82XX_HW_PX_MAP_CRB_PGN2 19 +#define QLA82XX_HW_PX_MAP_CRB_PGN3 20 +#define QLA82XX_HW_PX_MAP_CRB_PGN4 QLA82XX_HW_PX_MAP_CRB_SQS2 +#define QLA82XX_HW_PX_MAP_CRB_PGND 21 +#define QLA82XX_HW_PX_MAP_CRB_PGNI 22 +#define QLA82XX_HW_PX_MAP_CRB_PGS0 23 +#define QLA82XX_HW_PX_MAP_CRB_PGS1 24 +#define QLA82XX_HW_PX_MAP_CRB_PGS2 25 +#define QLA82XX_HW_PX_MAP_CRB_PGS3 26 +#define QLA82XX_HW_PX_MAP_CRB_PGSD 27 +#define QLA82XX_HW_PX_MAP_CRB_PGSI 28 +#define QLA82XX_HW_PX_MAP_CRB_SN 29 +#define QLA82XX_HW_PX_MAP_CRB_EG 31 +#define QLA82XX_HW_PX_MAP_CRB_PH2 32 +#define QLA82XX_HW_PX_MAP_CRB_PS2 33 +#define QLA82XX_HW_PX_MAP_CRB_CAM 34 +#define QLA82XX_HW_PX_MAP_CRB_CAS0 35 +#define QLA82XX_HW_PX_MAP_CRB_CAS1 36 +#define QLA82XX_HW_PX_MAP_CRB_CAS2 37 +#define QLA82XX_HW_PX_MAP_CRB_C2C0 38 +#define QLA82XX_HW_PX_MAP_CRB_C2C1 39 +#define QLA82XX_HW_PX_MAP_CRB_TIMR 40 +#define QLA82XX_HW_PX_MAP_CRB_RPMX1 42 +#define QLA82XX_HW_PX_MAP_CRB_RPMX2 43 +#define QLA82XX_HW_PX_MAP_CRB_RPMX3 44 +#define QLA82XX_HW_PX_MAP_CRB_RPMX4 45 +#define QLA82XX_HW_PX_MAP_CRB_RPMX5 46 +#define QLA82XX_HW_PX_MAP_CRB_RPMX6 47 +#define QLA82XX_HW_PX_MAP_CRB_RPMX7 48 +#define QLA82XX_HW_PX_MAP_CRB_XDMA 49 +#define QLA82XX_HW_PX_MAP_CRB_I2Q 50 +#define QLA82XX_HW_PX_MAP_CRB_ROMUSB 51 +#define QLA82XX_HW_PX_MAP_CRB_CAS3 52 +#define QLA82XX_HW_PX_MAP_CRB_RPMX0 53 +#define QLA82XX_HW_PX_MAP_CRB_RPMX8 54 +#define QLA82XX_HW_PX_MAP_CRB_RPMX9 55 +#define QLA82XX_HW_PX_MAP_CRB_OCM0 56 +#define QLA82XX_HW_PX_MAP_CRB_OCM1 57 +#define QLA82XX_HW_PX_MAP_CRB_SMB 58 +#define QLA82XX_HW_PX_MAP_CRB_I2C0 59 +#define QLA82XX_HW_PX_MAP_CRB_I2C1 60 +#define QLA82XX_HW_PX_MAP_CRB_LPC 61 +#define QLA82XX_HW_PX_MAP_CRB_PGNC 62 +#define QLA82XX_HW_PX_MAP_CRB_PGR0 63 +#define QLA82XX_HW_PX_MAP_CRB_PGR1 4 +#define QLA82XX_HW_PX_MAP_CRB_PGR2 30 +#define QLA82XX_HW_PX_MAP_CRB_PGR3 41 + +/* This field defines CRB adr [31:20] of the agents */ +/* */ + +#define QLA82XX_HW_CRB_HUB_AGT_ADR_MN ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \ + QLA82XX_HW_MN_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PH ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PH_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_MS ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \ + QLA82XX_HW_MS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX3 \ + ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_QMS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_QMS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS0 \ + ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | QLA82XX_HW_SQGS0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS1 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQGS1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS2 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQGS2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS3 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQGS3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_C2C0 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_C2C0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_C2C1 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_C2C1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX2 \ + ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX4 \ + ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX4_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX7 \ + ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX7_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX9 \ + ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX9_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SMB ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SMB_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_NIU ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \ + QLA82XX_HW_NIU_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2C0 ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \ + QLA82XX_HW_I2C0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2C1 ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \ + QLA82XX_HW_I2C1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SRE ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SRE_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_EG ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_EG_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX0 \ + ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_QMN ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_QM_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN2 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN3 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX1 \ + ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX5 \ + ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX5_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX6 \ + ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX6_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX8 \ + ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | QLA82XX_HW_RPMX8_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS2 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS3 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGNI ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGNI_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGND ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGND_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN0 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN1 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN2 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN3 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN4 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN4_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGNC ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGNC_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR0 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR1 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR2 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR3 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSI ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGSI_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSD ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGSD_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS0 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS1 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS2 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS3 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSC ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGSC_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAM ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_NCM_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_TMR_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_XDMA_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SN ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SN_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_I2Q_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB \ + ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | QLA82XX_HW_ROMUSB_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_OCM0 ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_OCM0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_OCM1 ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_OCM1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_LPC ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_LPC_CRB_AGT_ADR) + +#define ROMUSB_GLB (QLA82XX_CRB_ROMUSB + 0x00000) +#define QLA82XX_ROMUSB_GLB_PEGTUNE_DONE (ROMUSB_GLB + 0x005c) +#define QLA82XX_ROMUSB_GLB_STATUS (ROMUSB_GLB + 0x0004) +#define QLA82XX_ROMUSB_GLB_SW_RESET (ROMUSB_GLB + 0x0008) +#define QLA82XX_ROMUSB_ROM_ADDRESS (ROMUSB_ROM + 0x0008) +#define QLA82XX_ROMUSB_ROM_WDATA (ROMUSB_ROM + 0x000c) +#define QLA82XX_ROMUSB_ROM_ABYTE_CNT (ROMUSB_ROM + 0x0010) +#define QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT (ROMUSB_ROM + 0x0014) +#define QLA82XX_ROMUSB_ROM_RDATA (ROMUSB_ROM + 0x0018) + +#define ROMUSB_ROM (QLA82XX_CRB_ROMUSB + 0x10000) +#define QLA82XX_ROMUSB_ROM_INSTR_OPCODE (ROMUSB_ROM + 0x0004) +#define QLA82XX_ROMUSB_GLB_CAS_RST (ROMUSB_GLB + 0x0038) + +/* Lock IDs for ROM lock */ +#define ROM_LOCK_DRIVER 0x0d417340 + +#define QLA82XX_PCI_CRB_WINDOWSIZE 0x00100000 /* all are 1MB windows */ +#define QLA82XX_PCI_CRB_WINDOW(A) \ + (QLA82XX_PCI_CRBSPACE + (A)*QLA82XX_PCI_CRB_WINDOWSIZE) +#define QLA82XX_CRB_C2C_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C0) +#define QLA82XX_CRB_C2C_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C1) +#define QLA82XX_CRB_C2C_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C2) +#define QLA82XX_CRB_CAM \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAM) +#define QLA82XX_CRB_CASPER \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS) +#define QLA82XX_CRB_CASPER_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS0) +#define QLA82XX_CRB_CASPER_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS1) +#define QLA82XX_CRB_CASPER_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS2) +#define QLA82XX_CRB_DDR_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_MS) +#define QLA82XX_CRB_DDR_NET \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_MN) +#define QLA82XX_CRB_EPG \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_EG) +#define QLA82XX_CRB_I2Q \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2Q) +#define QLA82XX_CRB_NIU \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_NIU) + +#define QLA82XX_CRB_PCIX_HOST \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PH) +#define QLA82XX_CRB_PCIX_HOST2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PH2) +#define QLA82XX_CRB_PCIX_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PS) +#define QLA82XX_CRB_PCIE \ + QLA82XX_CRB_PCIX_MD + +/* window 1 pcie slot */ +#define QLA82XX_CRB_PCIE2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PS2) +#define QLA82XX_CRB_PEG_MD_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS0) +#define QLA82XX_CRB_PEG_MD_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS1) +#define QLA82XX_CRB_PEG_MD_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS2) +#define QLA82XX_CRB_PEG_MD_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS3) +#define QLA82XX_CRB_PEG_MD_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS3) +#define QLA82XX_CRB_PEG_MD_D \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGSD) +#define QLA82XX_CRB_PEG_MD_I \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGSI) +#define QLA82XX_CRB_PEG_NET_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN0) +#define QLA82XX_CRB_PEG_NET_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN1) +#define QLA82XX_CRB_PEG_NET_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN2) +#define QLA82XX_CRB_PEG_NET_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN3) +#define QLA82XX_CRB_PEG_NET_4 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN4) +#define QLA82XX_CRB_PEG_NET_D \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGND) +#define QLA82XX_CRB_PEG_NET_I \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGNI) +#define QLA82XX_CRB_PQM_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_QMS) +#define QLA82XX_CRB_PQM_NET \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_QMN) +#define QLA82XX_CRB_QDR_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SS) +#define QLA82XX_CRB_QDR_NET \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SN) +#define QLA82XX_CRB_ROMUSB \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_ROMUSB) +#define QLA82XX_CRB_RPMX_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX0) +#define QLA82XX_CRB_RPMX_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX1) +#define QLA82XX_CRB_RPMX_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX2) +#define QLA82XX_CRB_RPMX_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX3) +#define QLA82XX_CRB_RPMX_4 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX4) +#define QLA82XX_CRB_RPMX_5 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX5) +#define QLA82XX_CRB_RPMX_6 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX6) +#define QLA82XX_CRB_RPMX_7 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX7) +#define QLA82XX_CRB_SQM_MD_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS0) +#define QLA82XX_CRB_SQM_MD_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS1) +#define QLA82XX_CRB_SQM_MD_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS2) +#define QLA82XX_CRB_SQM_MD_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS3) +#define QLA82XX_CRB_SQM_NET_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN0) +#define QLA82XX_CRB_SQM_NET_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN1) +#define QLA82XX_CRB_SQM_NET_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN2) +#define QLA82XX_CRB_SQM_NET_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN3) +#define QLA82XX_CRB_SRE \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SRE) +#define QLA82XX_CRB_TIMER \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_TIMR) +#define QLA82XX_CRB_XDMA \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_XDMA) +#define QLA82XX_CRB_I2C0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2C0) +#define QLA82XX_CRB_I2C1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2C1) +#define QLA82XX_CRB_OCM0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_OCM0) +#define QLA82XX_CRB_SMB \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SMB) +#define QLA82XX_CRB_MAX \ + QLA82XX_PCI_CRB_WINDOW(64) + +/* + * ====================== BASE ADDRESSES ON-CHIP ====================== + * Base addresses of major components on-chip. + * ====================== BASE ADDRESSES ON-CHIP ====================== + */ +#define QLA82XX_ADDR_DDR_NET (0x0000000000000000ULL) +#define QLA82XX_ADDR_DDR_NET_MAX (0x000000000fffffffULL) + +/* Imbus address bit used to indicate a host address. This bit is + * eliminated by the pcie bar and bar select before presentation + * over pcie. */ +/* host memory via IMBUS */ +#define QLA82XX_P2_ADDR_PCIE (0x0000000800000000ULL) +#define QLA82XX_P3_ADDR_PCIE (0x0000008000000000ULL) +#define QLA82XX_ADDR_PCIE_MAX (0x0000000FFFFFFFFFULL) +#define QLA82XX_ADDR_OCM0 (0x0000000200000000ULL) +#define QLA82XX_ADDR_OCM0_MAX (0x00000002000fffffULL) +#define QLA82XX_ADDR_OCM1 (0x0000000200400000ULL) +#define QLA82XX_ADDR_OCM1_MAX (0x00000002004fffffULL) +#define QLA82XX_ADDR_QDR_NET (0x0000000300000000ULL) +#define QLA82XX_P3_ADDR_QDR_NET_MAX (0x0000000303ffffffULL) + +#define QLA82XX_PCI_CRBSPACE (unsigned long)0x06000000 +#define QLA82XX_PCI_DIRECT_CRB (unsigned long)0x04400000 +#define QLA82XX_PCI_CAMQM (unsigned long)0x04800000 +#define QLA82XX_PCI_CAMQM_MAX (unsigned long)0x04ffffff +#define QLA82XX_PCI_DDR_NET (unsigned long)0x00000000 +#define QLA82XX_PCI_QDR_NET (unsigned long)0x04000000 +#define QLA82XX_PCI_QDR_NET_MAX (unsigned long)0x043fffff + +/* + * + * Register offsets for MN + */ +#define MIU_CONTROL (0x000) +#define MIU_TAG (0x004) +#define MIU_TEST_AGT_CTRL (0x090) +#define MIU_TEST_AGT_ADDR_LO (0x094) +#define MIU_TEST_AGT_ADDR_HI (0x098) +#define MIU_TEST_AGT_WRDATA_LO (0x0a0) +#define MIU_TEST_AGT_WRDATA_HI (0x0a4) +#define MIU_TEST_AGT_WRDATA(i) (0x0a0+(4*(i))) +#define MIU_TEST_AGT_RDDATA_LO (0x0a8) +#define MIU_TEST_AGT_RDDATA_HI (0x0ac) +#define MIU_TEST_AGT_RDDATA(i) (0x0a8+(4*(i))) +#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8 +#define MIU_TEST_AGT_UPPER_ADDR(off) (0) + +/* MIU_TEST_AGT_CTRL flags. work for SIU as well */ +#define MIU_TA_CTL_START 1 +#define MIU_TA_CTL_ENABLE 2 +#define MIU_TA_CTL_WRITE 4 +#define MIU_TA_CTL_BUSY 8 + +/*CAM RAM */ +# define QLA82XX_CAM_RAM_BASE (QLA82XX_CRB_CAM + 0x02000) +# define QLA82XX_CAM_RAM(reg) (QLA82XX_CAM_RAM_BASE + (reg)) + +#define QLA82XX_PORT_MODE_ADDR (QLA82XX_CAM_RAM(0x24)) +#define QLA82XX_PEG_HALT_STATUS1 (QLA82XX_CAM_RAM(0xa8)) +#define QLA82XX_PEG_HALT_STATUS2 (QLA82XX_CAM_RAM(0xac)) +#define QLA82XX_PEG_ALIVE_COUNTER (QLA82XX_CAM_RAM(0xb0)) + +#define QLA82XX_CAMRAM_DB1 (QLA82XX_CAM_RAM(0x1b8)) +#define QLA82XX_CAMRAM_DB2 (QLA82XX_CAM_RAM(0x1bc)) + +#define HALT_STATUS_UNRECOVERABLE 0x80000000 +#define HALT_STATUS_RECOVERABLE 0x40000000 + +/* Driver Coexistence Defines */ +#define QLA82XX_CRB_DRV_ACTIVE (QLA82XX_CAM_RAM(0x138)) +#define QLA82XX_CRB_DEV_STATE (QLA82XX_CAM_RAM(0x140)) +#define QLA82XX_CRB_DRV_STATE (QLA82XX_CAM_RAM(0x144)) +#define QLA82XX_CRB_DRV_SCRATCH (QLA82XX_CAM_RAM(0x148)) +#define QLA82XX_CRB_DEV_PART_INFO (QLA82XX_CAM_RAM(0x14c)) +#define QLA82XX_CRB_DRV_IDC_VERSION (QLA82XX_CAM_RAM(0x174)) + +/* Every driver should use these Device State */ +#define QLA82XX_DEV_COLD 1 +#define QLA82XX_DEV_INITIALIZING 2 +#define QLA82XX_DEV_READY 3 +#define QLA82XX_DEV_NEED_RESET 4 +#define QLA82XX_DEV_NEED_QUIESCENT 5 +#define QLA82XX_DEV_FAILED 6 +#define QLA82XX_DEV_QUIESCENT 7 +#define MAX_STATES 8 + +#define QLA82XX_IDC_VERSION 1 +#define QLA82XX_ROM_DEV_INIT_TIMEOUT 30 +#define QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT 10 + +#define QLA82XX_ROM_LOCK_ID (QLA82XX_CAM_RAM(0x100)) +#define QLA82XX_CRB_WIN_LOCK_ID (QLA82XX_CAM_RAM(0x124)) +#define QLA82XX_FW_VERSION_MAJOR (QLA82XX_CAM_RAM(0x150)) +#define QLA82XX_FW_VERSION_MINOR (QLA82XX_CAM_RAM(0x154)) +#define QLA82XX_FW_VERSION_SUB (QLA82XX_CAM_RAM(0x158)) +#define QLA82XX_PCIE_REG(reg) (QLA82XX_CRB_PCIE + (reg)) + +#define PCIE_SETUP_FUNCTION (0x12040) +#define PCIE_SETUP_FUNCTION2 (0x12048) + +#define QLA82XX_PCIX_PS_REG(reg) (QLA82XX_CRB_PCIX_MD + (reg)) +#define QLA82XX_PCIX_PS2_REG(reg) (QLA82XX_CRB_PCIE2 + (reg)) + +#define PCIE_SEM2_LOCK (0x1c010) /* Flash lock */ +#define PCIE_SEM2_UNLOCK (0x1c014) /* Flash unlock */ +#define PCIE_SEM5_LOCK (0x1c028) /* Coexistence lock */ +#define PCIE_SEM5_UNLOCK (0x1c02c) /* Coexistence unlock */ +#define PCIE_SEM7_LOCK (0x1c038) /* crb win lock */ +#define PCIE_SEM7_UNLOCK (0x1c03c) /* crbwin unlock*/ + +/* Different drive state */ +#define QLA82XX_DRVST_NOT_RDY 0 +#define QLA82XX_DRVST_RST_RDY 1 +#define QLA82XX_DRVST_QSNT_RDY 2 + +/* Different drive active state */ +#define QLA82XX_DRV_NOT_ACTIVE 0 +#define QLA82XX_DRV_ACTIVE 1 + +/* + * The PCI VendorID and DeviceID for our board. + */ +#define PCI_DEVICE_ID_QLOGIC_ISP8021 0x8021 + +#define QLA82XX_MSIX_TBL_SPACE 8192 +#define QLA82XX_PCI_REG_MSIX_TBL 0x44 +#define QLA82XX_PCI_MSIX_CONTROL 0x40 + +struct crb_128M_2M_sub_block_map { + unsigned valid; + unsigned start_128M; + unsigned end_128M; + unsigned start_2M; +}; + +struct crb_128M_2M_block_map { + struct crb_128M_2M_sub_block_map sub_block[16]; +}; + +struct crb_addr_pair { + long addr; + long data; +}; + +#define ADDR_ERROR ((unsigned long) 0xffffffff) +#define MAX_CTL_CHECK 1000 + +/*************************************************************************** + * PCI related defines. + **************************************************************************/ + +/* + * Interrupt related defines. + */ +#define PCIX_TARGET_STATUS (0x10118) +#define PCIX_TARGET_STATUS_F1 (0x10160) +#define PCIX_TARGET_STATUS_F2 (0x10164) +#define PCIX_TARGET_STATUS_F3 (0x10168) +#define PCIX_TARGET_STATUS_F4 (0x10360) +#define PCIX_TARGET_STATUS_F5 (0x10364) +#define PCIX_TARGET_STATUS_F6 (0x10368) +#define PCIX_TARGET_STATUS_F7 (0x1036c) + +#define PCIX_TARGET_MASK (0x10128) +#define PCIX_TARGET_MASK_F1 (0x10170) +#define PCIX_TARGET_MASK_F2 (0x10174) +#define PCIX_TARGET_MASK_F3 (0x10178) +#define PCIX_TARGET_MASK_F4 (0x10370) +#define PCIX_TARGET_MASK_F5 (0x10374) +#define PCIX_TARGET_MASK_F6 (0x10378) +#define PCIX_TARGET_MASK_F7 (0x1037c) + +/* + * Message Signaled Interrupts + */ +#define PCIX_MSI_F0 (0x13000) +#define PCIX_MSI_F1 (0x13004) +#define PCIX_MSI_F2 (0x13008) +#define PCIX_MSI_F3 (0x1300c) +#define PCIX_MSI_F4 (0x13010) +#define PCIX_MSI_F5 (0x13014) +#define PCIX_MSI_F6 (0x13018) +#define PCIX_MSI_F7 (0x1301c) +#define PCIX_MSI_F(FUNC) (0x13000 + ((FUNC) * 4)) +#define PCIX_INT_VECTOR (0x10100) +#define PCIX_INT_MASK (0x10104) + +/* + * Interrupt state machine and other bits. + */ +#define PCIE_MISCCFG_RC (0x1206c) + +#define ISR_INT_TARGET_STATUS \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS)) +#define ISR_INT_TARGET_STATUS_F1 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F1)) +#define ISR_INT_TARGET_STATUS_F2 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F2)) +#define ISR_INT_TARGET_STATUS_F3 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F3)) +#define ISR_INT_TARGET_STATUS_F4 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F4)) +#define ISR_INT_TARGET_STATUS_F5 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F5)) +#define ISR_INT_TARGET_STATUS_F6 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F6)) +#define ISR_INT_TARGET_STATUS_F7 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F7)) + +#define ISR_INT_TARGET_MASK \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK)) +#define ISR_INT_TARGET_MASK_F1 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F1)) +#define ISR_INT_TARGET_MASK_F2 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F2)) +#define ISR_INT_TARGET_MASK_F3 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F3)) +#define ISR_INT_TARGET_MASK_F4 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F4)) +#define ISR_INT_TARGET_MASK_F5 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F5)) +#define ISR_INT_TARGET_MASK_F6 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F6)) +#define ISR_INT_TARGET_MASK_F7 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F7)) + +#define ISR_INT_VECTOR \ + (QLA82XX_PCIX_PS_REG(PCIX_INT_VECTOR)) +#define ISR_INT_MASK \ + (QLA82XX_PCIX_PS_REG(PCIX_INT_MASK)) +#define ISR_INT_STATE_REG \ + (QLA82XX_PCIX_PS_REG(PCIE_MISCCFG_RC)) + +#define ISR_MSI_INT_TRIGGER(FUNC) \ + (QLA82XX_PCIX_PS_REG(PCIX_MSI_F(FUNC))) + +#define ISR_IS_LEGACY_INTR_IDLE(VAL) (((VAL) & 0x300) == 0) +#define ISR_IS_LEGACY_INTR_TRIGGERED(VAL) (((VAL) & 0x300) == 0x200) + +/* + * PCI Interrupt Vector Values. + */ +#define PCIX_INT_VECTOR_BIT_F0 0x0080 +#define PCIX_INT_VECTOR_BIT_F1 0x0100 +#define PCIX_INT_VECTOR_BIT_F2 0x0200 +#define PCIX_INT_VECTOR_BIT_F3 0x0400 +#define PCIX_INT_VECTOR_BIT_F4 0x0800 +#define PCIX_INT_VECTOR_BIT_F5 0x1000 +#define PCIX_INT_VECTOR_BIT_F6 0x2000 +#define PCIX_INT_VECTOR_BIT_F7 0x4000 + +struct qla82xx_legacy_intr_set { + uint32_t int_vec_bit; + uint32_t tgt_status_reg; + uint32_t tgt_mask_reg; + uint32_t pci_int_reg; +}; + +#define QLA82XX_LEGACY_INTR_CONFIG \ +{ \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F0, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(0) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F1, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F1, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F1, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(1) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F2, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F2, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F2, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(2) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F3, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F3, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F3, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(3) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F4, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F4, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F4, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(4) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F5, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F5, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F5, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(5) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F6, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F6, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F6, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(6) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F7, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F7, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F7, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(7) }, \ +} + +#define BRDCFG_START 0x4000 +#define BOOTLD_START 0x10000 +#define IMAGE_START 0x100000 +#define FLASH_ADDR_START 0x43000 + +/* Magic number to let user know flash is programmed */ +#define QLA82XX_BDINFO_MAGIC 0x12345678 +#define QLA82XX_FW_MAGIC_OFFSET (BRDCFG_START + 0x128) +#define FW_SIZE_OFFSET (0x3e840c) +#define QLA82XX_FW_MIN_SIZE 0x3fffff + +/* UNIFIED ROMIMAGE START */ +#define QLA82XX_URI_FW_MIN_SIZE 0xc8000 +#define QLA82XX_URI_DIR_SECT_PRODUCT_TBL 0x0 +#define QLA82XX_URI_DIR_SECT_BOOTLD 0x6 +#define QLA82XX_URI_DIR_SECT_FW 0x7 + +/* Offsets */ +#define QLA82XX_URI_CHIP_REV_OFF 10 +#define QLA82XX_URI_FLAGS_OFF 11 +#define QLA82XX_URI_BIOS_VERSION_OFF 12 +#define QLA82XX_URI_BOOTLD_IDX_OFF 27 +#define QLA82XX_URI_FIRMWARE_IDX_OFF 29 + +struct qla82xx_uri_table_desc{ + uint32_t findex; + uint32_t num_entries; + uint32_t entry_size; + uint32_t reserved[5]; +}; + +struct qla82xx_uri_data_desc{ + uint32_t findex; + uint32_t size; + uint32_t reserved[5]; +}; + +/* UNIFIED ROMIMAGE END */ + +#define QLA82XX_UNIFIED_ROMIMAGE 3 +#define QLA82XX_FLASH_ROMIMAGE 4 +#define QLA82XX_UNKNOWN_ROMIMAGE 0xff + +#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x0b0) +#define MIU_TEST_AGT_WRDATA_UPPER_HI (0x0b4) + +#ifndef readq +static inline u64 readq(void __iomem *addr) +{ + return readl(addr) | (((u64) readl(addr + 4)) << 32LL); +} +#endif + +#ifndef writeq +static inline void writeq(u64 val, void __iomem *addr) +{ + writel(((u32) (val)), (addr)); + writel(((u32) (val >> 32)), (addr + 4)); +} +#endif + +/* Request and response queue size */ +#define REQUEST_ENTRY_CNT_82XX 128 /* Number of request entries. */ +#define RESPONSE_ENTRY_CNT_82XX 128 /* Number of response entries.*/ + +/* + * ISP 8021 I/O Register Set structure definitions. + */ +struct device_reg_82xx { + uint32_t req_q_out[64]; /* Request Queue out-Pointer (64 * 4) */ + uint32_t rsp_q_in[64]; /* Response Queue In-Pointer. */ + uint32_t rsp_q_out[64]; /* Response Queue Out-Pointer. */ + + uint16_t mailbox_in[32]; /* Mail box In registers */ + uint16_t unused_1[32]; + uint32_t hint; /* Host interrupt register */ +#define HINT_MBX_INT_PENDING BIT_0 + uint16_t unused_2[62]; + uint16_t mailbox_out[32]; /* Mail box Out registers */ + uint32_t unused_3[48]; + + uint32_t host_status; /* host status */ +#define HSRX_RISC_INT BIT_15 /* RISC to Host interrupt. */ +#define HSRX_RISC_PAUSED BIT_8 /* RISC Paused. */ + uint32_t host_int; /* Interrupt status. */ +#define ISRX_NX_RISC_INT BIT_0 /* RISC interrupt. */ +}; + +struct fcp_cmnd { + struct scsi_lun lun; + uint8_t crn; + uint8_t task_attribute; + uint8_t task_management; + uint8_t additional_cdb_len; + uint8_t cdb[260]; /* 256 for CDB len and 4 for FCP_DL */ +}; + +struct dsd_dma { + struct list_head list; + dma_addr_t dsd_list_dma; + void *dsd_addr; +}; + +#define QLA_DSDS_PER_IOCB 37 +#define QLA_DSD_SIZE 12 +struct ct6_dsd { + uint16_t fcp_cmnd_len; + dma_addr_t fcp_cmnd_dma; + struct fcp_cmnd *fcp_cmnd; + int dsd_use_cnt; + struct list_head dsd_list; +}; + +#define MBC_TOGGLE_INTERRUPT 0x10 +#define MBC_SET_LED_CONFIG 0x125 /* FCoE specific LED control */ +#define MBC_GET_LED_CONFIG 0x126 /* FCoE specific LED control */ + +/* Flash offset */ +#define FLT_REG_BOOTLOAD_82XX 0x72 +#define FLT_REG_BOOT_CODE_82XX 0x78 +#define FLT_REG_FW_82XX 0x74 +#define FLT_REG_GOLD_FW_82XX 0x75 +#define FLT_REG_VPD_82XX 0x81 + +#define FA_VPD_SIZE_82XX 0x400 + +#define FA_FLASH_LAYOUT_ADDR_82 0xFC400 + +/****************************************************************************** +* +* Definitions specific to M25P flash +* +******************************************************************************* +* Instructions +*/ +#define M25P_INSTR_WREN 0x06 +#define M25P_INSTR_WRDI 0x04 +#define M25P_INSTR_RDID 0x9f +#define M25P_INSTR_RDSR 0x05 +#define M25P_INSTR_WRSR 0x01 +#define M25P_INSTR_READ 0x03 +#define M25P_INSTR_FAST_READ 0x0b +#define M25P_INSTR_PP 0x02 +#define M25P_INSTR_SE 0xd8 +#define M25P_INSTR_BE 0xc7 +#define M25P_INSTR_DP 0xb9 +#define M25P_INSTR_RES 0xab + +/* Minidump related */ + +/* + * Version of the template + * 4 Bytes + * X.Major.Minor.RELEASE + */ +#define QLA82XX_MINIDUMP_VERSION 0x10101 + +/* + * Entry Type Defines + */ +#define QLA82XX_RDNOP 0 +#define QLA82XX_RDCRB 1 +#define QLA82XX_RDMUX 2 +#define QLA82XX_QUEUE 3 +#define QLA82XX_BOARD 4 +#define QLA82XX_RDSRE 5 +#define QLA82XX_RDOCM 6 +#define QLA82XX_CACHE 10 +#define QLA82XX_L1DAT 11 +#define QLA82XX_L1INS 12 +#define QLA82XX_L2DTG 21 +#define QLA82XX_L2ITG 22 +#define QLA82XX_L2DAT 23 +#define QLA82XX_L2INS 24 +#define QLA82XX_RDROM 71 +#define QLA82XX_RDMEM 72 +#define QLA82XX_CNTRL 98 +#define QLA82XX_TLHDR 99 +#define QLA82XX_RDEND 255 + +/* + * Opcodes for Control Entries. + * These Flags are bit fields. + */ +#define QLA82XX_DBG_OPCODE_WR 0x01 +#define QLA82XX_DBG_OPCODE_RW 0x02 +#define QLA82XX_DBG_OPCODE_AND 0x04 +#define QLA82XX_DBG_OPCODE_OR 0x08 +#define QLA82XX_DBG_OPCODE_POLL 0x10 +#define QLA82XX_DBG_OPCODE_RDSTATE 0x20 +#define QLA82XX_DBG_OPCODE_WRSTATE 0x40 +#define QLA82XX_DBG_OPCODE_MDSTATE 0x80 + +/* + * Template Header and Entry Header definitions start here. + */ + +/* + * Template Header + * Parts of the template header can be modified by the driver. + * These include the saved_state_array, capture_debug_level, driver_timestamp + */ + +#define QLA82XX_DBG_STATE_ARRAY_LEN 16 +#define QLA82XX_DBG_CAP_SIZE_ARRAY_LEN 8 +#define QLA82XX_DBG_RSVD_ARRAY_LEN 8 + +/* + * Driver Flags + */ +#define QLA82XX_DBG_SKIPPED_FLAG 0x80 /* driver skipped this entry */ +#define QLA82XX_DEFAULT_CAP_MASK 0xFF /* default capture mask */ + +struct qla82xx_md_template_hdr { + uint32_t entry_type; + uint32_t first_entry_offset; + uint32_t size_of_template; + uint32_t capture_debug_level; + + uint32_t num_of_entries; + uint32_t version; + uint32_t driver_timestamp; + uint32_t template_checksum; + + uint32_t driver_capture_mask; + uint32_t driver_info[3]; + + uint32_t saved_state_array[QLA82XX_DBG_STATE_ARRAY_LEN]; + uint32_t capture_size_array[QLA82XX_DBG_CAP_SIZE_ARRAY_LEN]; + + /* markers_array used to capture some special locations on board */ + uint32_t markers_array[QLA82XX_DBG_RSVD_ARRAY_LEN]; + uint32_t num_of_free_entries; /* For internal use */ + uint32_t free_entry_offset; /* For internal use */ + uint32_t total_table_size; /* For internal use */ + uint32_t bkup_table_offset; /* For internal use */ +} __packed; + +/* + * Entry Header: Common to All Entry Types + */ + +/* + * Driver Code is for driver to write some info about the entry. + * Currently not used. + */ +typedef struct qla82xx_md_entry_hdr { + uint32_t entry_type; + uint32_t entry_size; + uint32_t entry_capture_size; + struct { + uint8_t entry_capture_mask; + uint8_t entry_code; + uint8_t driver_code; + uint8_t driver_flags; + } d_ctrl; +} __packed qla82xx_md_entry_hdr_t; + +/* + * Read CRB entry header + */ +struct qla82xx_md_entry_crb { + qla82xx_md_entry_hdr_t h; + uint32_t addr; + struct { + uint8_t addr_stride; + uint8_t state_index_a; + uint16_t poll_timeout; + } crb_strd; + + uint32_t data_size; + uint32_t op_count; + + struct { + uint8_t opcode; + uint8_t state_index_v; + uint8_t shl; + uint8_t shr; + } crb_ctrl; + + uint32_t value_1; + uint32_t value_2; + uint32_t value_3; +} __packed; + +/* + * Cache entry header + */ +struct qla82xx_md_entry_cache { + qla82xx_md_entry_hdr_t h; + + uint32_t tag_reg_addr; + struct { + uint16_t tag_value_stride; + uint16_t init_tag_value; + } addr_ctrl; + + uint32_t data_size; + uint32_t op_count; + + uint32_t control_addr; + struct { + uint16_t write_value; + uint8_t poll_mask; + uint8_t poll_wait; + } cache_ctrl; + + uint32_t read_addr; + struct { + uint8_t read_addr_stride; + uint8_t read_addr_cnt; + uint16_t rsvd_1; + } read_ctrl; +} __packed; + +/* + * Read OCM + */ +struct qla82xx_md_entry_rdocm { + qla82xx_md_entry_hdr_t h; + + uint32_t rsvd_0; + uint32_t rsvd_1; + uint32_t data_size; + uint32_t op_count; + + uint32_t rsvd_2; + uint32_t rsvd_3; + uint32_t read_addr; + uint32_t read_addr_stride; + uint32_t read_addr_cntrl; +} __packed; + +/* + * Read Memory + */ +struct qla82xx_md_entry_rdmem { + qla82xx_md_entry_hdr_t h; + uint32_t rsvd[6]; + uint32_t read_addr; + uint32_t read_data_size; +} __packed; + +/* + * Read ROM + */ +struct qla82xx_md_entry_rdrom { + qla82xx_md_entry_hdr_t h; + uint32_t rsvd[6]; + uint32_t read_addr; + uint32_t read_data_size; +} __packed; + +struct qla82xx_md_entry_mux { + qla82xx_md_entry_hdr_t h; + + uint32_t select_addr; + uint32_t rsvd_0; + uint32_t data_size; + uint32_t op_count; + + uint32_t select_value; + uint32_t select_value_stride; + uint32_t read_addr; + uint32_t rsvd_1; +} __packed; + +struct qla82xx_md_entry_queue { + qla82xx_md_entry_hdr_t h; + + uint32_t select_addr; + struct { + uint16_t queue_id_stride; + uint16_t rsvd_0; + } q_strd; + + uint32_t data_size; + uint32_t op_count; + uint32_t rsvd_1; + uint32_t rsvd_2; + + uint32_t read_addr; + struct { + uint8_t read_addr_stride; + uint8_t read_addr_cnt; + uint16_t rsvd_3; + } rd_strd; +} __packed; + +#define MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE 0x129 +#define RQST_TMPLT_SIZE 0x0 +#define RQST_TMPLT 0x1 +#define MD_DIRECT_ROM_WINDOW 0x42110030 +#define MD_DIRECT_ROM_READ_BASE 0x42150000 +#define MD_MIU_TEST_AGT_CTRL 0x41000090 +#define MD_MIU_TEST_AGT_ADDR_LO 0x41000094 +#define MD_MIU_TEST_AGT_ADDR_HI 0x41000098 + +static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, 0x410000AC, + 0x410000B8, 0x410000BC }; + +#define CRB_NIU_XG_PAUSE_CTL_P0 0x1 +#define CRB_NIU_XG_PAUSE_CTL_P1 0x8 + +#define qla82xx_get_temp_val(x) ((x) >> 16) +#define qla82xx_get_temp_state(x) ((x) & 0xffff) +#define qla82xx_encode_temp(val, state) (((val) << 16) | (state)) + +/* + * Temperature control. + */ +enum { + QLA82XX_TEMP_NORMAL = 0x1, /* Normal operating range */ + QLA82XX_TEMP_WARN, /* Sound alert, temperature getting high */ + QLA82XX_TEMP_PANIC /* Fatal error, hardware has shut down. */ +}; +#endif diff --git a/qla2x00t/qla_os.c b/qla2x00t/qla_os.c index 5c7018eaf..b9a07695d 100644 --- a/qla2x00t/qla_os.c +++ b/qla2x00t/qla_os.c @@ -1,9 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ +#include "qla_def.h" + #include #include #include @@ -20,9 +22,6 @@ #include #include -#include "qla_def.h" -#include "qla2x_tgt.h" - #ifndef RHEL_RELEASE_VERSION #define RHEL_RELEASE_VERSION(maj, min) 0 #endif @@ -33,6 +32,8 @@ char qla2x00_version_str[40]; #ifdef CONFIG_SCSI_QLA2XXX_TARGET +#include "qla2x_tgt.h" + /* * Target mode add-on's callbacks */ @@ -55,21 +56,31 @@ MODULE_PARM_DESC(qlini_mode, "\"enabled\" - initiator mode will always stay enabled."); static int ql2x_ini_mode = QLA2X_INI_MODE_EXCLUSIVE; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + +static int apidev_major; /* * SRB allocation cache */ static struct kmem_cache *srb_cachep; -int num_hosts; +/* + * CT6 CTX allocation cache + */ +static struct kmem_cache *ctx_cachep; +/* + * error level for logging + */ +int ql_errlev = ql_log_all; + int ql2xlogintimeout = 20; -module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR); +module_param(ql2xlogintimeout, int, S_IRUGO); MODULE_PARM_DESC(ql2xlogintimeout, "Login timeout value in seconds."); int qlport_down_retry; -module_param(qlport_down_retry, int, S_IRUGO|S_IRUSR); +module_param(qlport_down_retry, int, S_IRUGO); MODULE_PARM_DESC(qlport_down_retry, "Maximum number of command retries to a port that returns " "a PORT-DOWN status."); @@ -82,35 +93,45 @@ MODULE_PARM_DESC(ql2xplogiabsentdevice, "Default is 0 - no PLOGI. 1 - perfom PLOGI."); int ql2xloginretrycount = 0; -module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR); +module_param(ql2xloginretrycount, int, S_IRUGO); MODULE_PARM_DESC(ql2xloginretrycount, "Specify an alternate value for the NVRAM login retry count."); -int ql2xenableclass2; -module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR); -MODULE_PARM_DESC(ql2xenableclass2, - "Specify if Class 2 operations are supported from the very " - "beginning."); - -int ql2xallocfwdump; -module_param(ql2xallocfwdump, int, S_IRUGO|S_IRUSR); +int ql2xallocfwdump = 1; +module_param(ql2xallocfwdump, int, S_IRUGO); MODULE_PARM_DESC(ql2xallocfwdump, "Option to enable allocation of memory for a firmware dump " "during HBA initialization. Memory allocation requirements " - "vary by ISP type. Default is 0 - don't allocate memory."); + "vary by ISP type. Default is 1 - allocate memory."); int ql2xextended_error_logging; module_param(ql2xextended_error_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql2xextended_error_logging, - "Option to enable extended error logging, " - "Default is 0 - no logging. 1 - log errors."); + "Option to enable extended error logging,\n" + "\t\tDefault is 0 - no logging. 0x40000000 - Module Init & Probe.\n" + "\t\t0x20000000 - Mailbox Cmnds. 0x10000000 - Device Discovery.\n" + "\t\t0x08000000 - IO tracing. 0x04000000 - DPC Thread.\n" + "\t\t0x02000000 - Async events. 0x01000000 - Timer routines.\n" + "\t\t0x00800000 - User space. 0x00400000 - Task Management.\n" + "\t\t0x00200000 - AER/EEH. 0x00100000 - Multi Q.\n" + "\t\t0x00080000 - P3P Specific. 0x00040000 - Virtual Port.\n" + "\t\t0x00020000 - Buffer Dump. 0x00010000 - Misc.\n" + "\t\t0x7fffffff - For enabling all logs, can be too many logs.\n" + "\t\t0x1e400000 - Preferred value for capturing essential " + "debug information (equivalent to old " + "ql2xextended_error_logging=1).\n" + "\t\tDo LOGICAL OR of the value to enable more than one level"); + +int ql2xshiftctondsd = 6; +module_param(ql2xshiftctondsd, int, S_IRUGO); +MODULE_PARM_DESC(ql2xshiftctondsd, + "Set to control shifting of command type processing " + "based on total number of SG elements."); static void qla2x00_free_device(scsi_qla_host_t *); -static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha); - int ql2xfdmienable=1; -module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR); +module_param(ql2xfdmienable, int, S_IRUGO); MODULE_PARM_DESC(ql2xfdmienable, "Enables FDMI registrations " "Default is 1 - perform FDMI. 0 - no FDMI."); @@ -121,12 +142,108 @@ module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql2xmaxqdepth, "Maximum queue depth to report for target devices."); -int ql2xqfullrampup = 120; -module_param(ql2xqfullrampup, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(ql2xqfullrampup, - "Number of seconds to wait to begin to ramp-up the queue " - "depth for a device after a queue-full condition has been " - "detected. Default is 120 seconds."); +/* Do not change the value of this after module load */ +int ql2xenabledif = 0; +module_param(ql2xenabledif, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xenabledif, + " Enable T10-CRC-DIF " + " Default is 0 - No DIF Support. 1 - Enable it" + ", 2 - Enable DIF for all types, except Type 0."); + +int ql2xenablehba_err_chk = 2; +module_param(ql2xenablehba_err_chk, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xenablehba_err_chk, + " Enable T10-CRC-DIF Error isolation by HBA:\n" + " Default is 1.\n" + " 0 -- Error isolation disabled\n" + " 1 -- Error isolation enabled only for DIX Type 0\n" + " 2 -- Error isolation enabled for all Types\n"); + +int ql2xiidmaenable=1; +module_param(ql2xiidmaenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xiidmaenable, + "Enables iIDMA settings " + "Default is 1 - perform iIDMA. 0 - no iIDMA."); + +int ql2xmaxqueues = 1; +module_param(ql2xmaxqueues, int, S_IRUGO); +MODULE_PARM_DESC(ql2xmaxqueues, + "Enables MQ settings " + "Default is 1 for single queue. Set it to number " + "of queues in MQ mode."); + +int ql2xmultique_tag; +module_param(ql2xmultique_tag, int, S_IRUGO); +MODULE_PARM_DESC(ql2xmultique_tag, + "Enables CPU affinity settings for the driver " + "Default is 0 for no affinity of request and response IO. " + "Set it to 1 to turn on the cpu affinity."); + +int ql2xfwloadbin; +module_param(ql2xfwloadbin, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xfwloadbin, + "Option to specify location from which to load ISP firmware:.\n" + " 2 -- load firmware via the request_firmware() (hotplug).\n" + " interface.\n" + " 1 -- load firmware from flash.\n" + " 0 -- use default semantics.\n"); + +int ql2xetsenable; +module_param(ql2xetsenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xetsenable, + "Enables firmware ETS burst." + "Default is 0 - skip ETS enablement."); + +int ql2xdbwr = 1; +module_param(ql2xdbwr, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xdbwr, + "Option to specify scheme for request queue posting.\n" + " 0 -- Regular doorbell.\n" + " 1 -- CAMRAM doorbell (faster).\n"); + +int ql2xdontresethba; +module_param(ql2xdontresethba, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xdontresethba, + "Option to specify reset behaviour\n" + " 0 (Default) -- Reset on failure.\n" + " 1 -- Do not reset on failure.\n"); + +uint ql2xmaxlun = MAX_LUNS; +module_param(ql2xmaxlun, uint, S_IRUGO); +MODULE_PARM_DESC(ql2xmaxlun, + "Defines the maximum LU number to register with the SCSI " + "midlayer. Default is 65535."); + +int ql2xtargetreset = 1; +module_param(ql2xtargetreset, int, S_IRUGO); +MODULE_PARM_DESC(ql2xtargetreset, + "Enable target reset." + "Default is 1 - use hw defaults."); + +int ql2xgffidenable = 0; +module_param(ql2xgffidenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xgffidenable, + "Enables GFF_ID checks of port type. " + "Default is 0 - Do not use GFF_ID information."); + +int ql2xasynctmfenable; +module_param(ql2xasynctmfenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xasynctmfenable, + "Enables issue of TM IOCBs asynchronously via IOCB mechanism" + "Default is 0 - Issue TM IOCBs via mailbox mechanism."); + +int ql2xmdcapmask = 0x1F; +module_param(ql2xmdcapmask, int, S_IRUGO); +MODULE_PARM_DESC(ql2xmdcapmask, + "Set the Minidump driver capture mask level. " + "Default is 0x1F - Can be set to 0x3, 0x7, 0xF, 0x1F, 0x7F."); + +int ql2xmdenable = 1; +module_param(ql2xmdenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xmdenable, + "Enable/disable MiniDump. " + "0 - MiniDump disabled. " + "1 (Default) - MiniDump enabled."); /* * SCSI host template entry points @@ -136,13 +253,10 @@ static int qla2xxx_slave_alloc(struct scsi_device *); static int qla2xxx_scan_finished(struct Scsi_Host *, unsigned long time); static void qla2xxx_scan_start(struct Scsi_Host *); static void qla2xxx_slave_destroy(struct scsi_device *); -static int qla2x00_queuecommand_lck(struct scsi_cmnd *cmd, - void (*fn)(struct scsi_cmnd *)); -static int qla24xx_queuecommand_lck(struct scsi_cmnd *cmd, +static int qla2xxx_queuecommand_lck(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) -static DEF_SCSI_QCMD(qla2x00_queuecommand); -static DEF_SCSI_QCMD(qla24xx_queuecommand); +static DEF_SCSI_QCMD(qla2xxx_queuecommand); #endif static int qla2xxx_eh_abort(struct scsi_cmnd *); static int qla2xxx_eh_device_reset(struct scsi_cmnd *); @@ -150,24 +264,26 @@ static int qla2xxx_eh_target_reset(struct scsi_cmnd *); static int qla2xxx_eh_bus_reset(struct scsi_cmnd *); static int qla2xxx_eh_host_reset(struct scsi_cmnd *); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && \ !defined(CONFIG_SUSE_KERNEL) && \ (!defined(RHEL_RELEASE_CODE) || \ RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) static int qla2x00_change_queue_depth(struct scsi_device *, int); #else -static int qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason); +static int qla2x00_change_queue_depth(struct scsi_device *, int, int); #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) static int qla2x00_change_queue_type(struct scsi_device *, int); +#endif -static struct scsi_host_template qla2x00_driver_template = { +struct scsi_host_template qla2xxx_driver_template = { .module = THIS_MODULE, .name = QLA2XXX_DRIVER_NAME, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) - .queuecommand = qla2x00_queuecommand_lck, + .queuecommand = qla2xxx_queuecommand_lck, #else - .queuecommand = qla2x00_queuecommand, + .queuecommand = qla2xxx_queuecommand, #endif .eh_abort_handler = qla2xxx_eh_abort, @@ -182,48 +298,10 @@ static struct scsi_host_template qla2x00_driver_template = { .slave_destroy = qla2xxx_slave_destroy, .scan_finished = qla2xxx_scan_finished, .scan_start = qla2xxx_scan_start, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) .change_queue_depth = qla2x00_change_queue_depth, .change_queue_type = qla2x00_change_queue_type, - .this_id = -1, - .cmd_per_lun = 3, - .use_clustering = ENABLE_CLUSTERING, - .sg_tablesize = SG_ALL, - - /* - * The RISC allows for each command to transfer (2^32-1) bytes of data, - * which equates to 0x800000 sectors. - */ - .max_sectors = 0xFFFF, - .shost_attrs = qla2x00_host_attrs, - -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - .supported_mode = MODE_INITIATOR | MODE_TARGET, #endif -}; - -struct scsi_host_template qla24xx_driver_template = { - .module = THIS_MODULE, - .name = QLA2XXX_DRIVER_NAME, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) - .queuecommand = qla24xx_queuecommand_lck, -#else - .queuecommand = qla24xx_queuecommand, -#endif - - .eh_abort_handler = qla2xxx_eh_abort, - .eh_device_reset_handler = qla2xxx_eh_device_reset, - .eh_target_reset_handler = qla2xxx_eh_target_reset, - .eh_bus_reset_handler = qla2xxx_eh_bus_reset, - .eh_host_reset_handler = qla2xxx_eh_host_reset, - - .slave_configure = qla2xxx_slave_configure, - - .slave_alloc = qla2xxx_slave_alloc, - .slave_destroy = qla2xxx_slave_destroy, - .scan_finished = qla2xxx_scan_finished, - .scan_start = qla2xxx_scan_start, - .change_queue_depth = qla2x00_change_queue_depth, - .change_queue_type = qla2x00_change_queue_type, .this_id = -1, .cmd_per_lun = 3, .use_clustering = ENABLE_CLUSTERING, @@ -231,10 +309,9 @@ struct scsi_host_template qla24xx_driver_template = { .max_sectors = 0xFFFF, .shost_attrs = qla2x00_host_attrs, - #ifdef CONFIG_SCSI_QLA2XXX_TARGET .supported_mode = MODE_INITIATOR | MODE_TARGET, -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ }; static struct scsi_transport_template *qla2xxx_transport_template = NULL; @@ -246,42 +323,185 @@ struct scsi_transport_template *qla2xxx_transport_vport_template = NULL; */ __inline__ void -qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval) +qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval) { - init_timer(&ha->timer); - ha->timer.expires = jiffies + interval * HZ; - ha->timer.data = (unsigned long)ha; - ha->timer.function = (void (*)(unsigned long))func; - add_timer(&ha->timer); - ha->timer_active = 1; + init_timer(&vha->timer); + vha->timer.expires = jiffies + interval * HZ; + vha->timer.data = (unsigned long)vha; + vha->timer.function = (void (*)(unsigned long))func; + add_timer(&vha->timer); + vha->timer_active = 1; } static inline void -qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval) +qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval) { - mod_timer(&ha->timer, jiffies + interval * HZ); + /* Currently used for 82XX only. */ + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_dbg(ql_dbg_timer, vha, 0x600d, + "Device in a failed state, returning.\n"); + return; + } + + mod_timer(&vha->timer, jiffies + interval * HZ); } static __inline__ void -qla2x00_stop_timer(scsi_qla_host_t *ha) +qla2x00_stop_timer(scsi_qla_host_t *vha) { - del_timer_sync(&ha->timer); - ha->timer_active = 0; + del_timer_sync(&vha->timer); + vha->timer_active = 0; } static int qla2x00_do_dpc(void *data); static void qla2x00_rst_aen(scsi_qla_host_t *); -static int qla2x00_mem_alloc(scsi_qla_host_t *); -static void qla2x00_mem_free(scsi_qla_host_t *ha); -static void qla2x00_sp_free_dma(scsi_qla_host_t *, srb_t *); +static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t, + struct req_que **, struct rsp_que **); +static void qla2x00_free_fw_dump(struct qla_hw_data *); +static void qla2x00_mem_free(struct qla_hw_data *); /* -------------------------------------------------------------------------- */ +static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req, + struct rsp_que *rsp) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues, + GFP_KERNEL); + if (!ha->req_q_map) { + ql_log(ql_log_fatal, vha, 0x003b, + "Unable to allocate memory for request queue ptrs.\n"); + goto fail_req_map; + } + + ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues, + GFP_KERNEL); + if (!ha->rsp_q_map) { + ql_log(ql_log_fatal, vha, 0x003c, + "Unable to allocate memory for response queue ptrs.\n"); + goto fail_rsp_map; + } + /* + * Make sure we record at least the request and response queue zero in + * case we need to free them if part of the probe fails. + */ + ha->rsp_q_map[0] = rsp; + ha->req_q_map[0] = req; + set_bit(0, ha->rsp_qid_map); + set_bit(0, ha->req_qid_map); + return 1; + +fail_rsp_map: + kfree(ha->req_q_map); + ha->req_q_map = NULL; +fail_req_map: + return -ENOMEM; +} + +static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) +{ + if (req && req->ring) + dma_free_coherent(&ha->pdev->dev, + (req->length + 1) * sizeof(request_t), + req->ring, req->dma); + + kfree(req); + req = NULL; +} + +static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp) +{ + if (rsp && rsp->ring) + dma_free_coherent(&ha->pdev->dev, + (rsp->length + 1) * sizeof(response_t), + rsp->ring, rsp->dma); + + kfree(rsp); + rsp = NULL; +} + +static void qla2x00_free_queues(struct qla_hw_data *ha) +{ + struct req_que *req; + struct rsp_que *rsp; + int cnt; + + for (cnt = 0; cnt < ha->max_req_queues; cnt++) { + req = ha->req_q_map[cnt]; + qla2x00_free_req_que(ha, req); + } + kfree(ha->req_q_map); + ha->req_q_map = NULL; + + for (cnt = 0; cnt < ha->max_rsp_queues; cnt++) { + rsp = ha->rsp_q_map[cnt]; + qla2x00_free_rsp_que(ha, rsp); + } + kfree(ha->rsp_q_map); + ha->rsp_q_map = NULL; +} + +static int qla25xx_setup_mode(struct scsi_qla_host *vha) +{ + uint16_t options = 0; + int ques, req, ret; + struct qla_hw_data *ha = vha->hw; + + if (!(ha->fw_attributes & BIT_6)) { + ql_log(ql_log_warn, vha, 0x00d8, + "Firmware is not multi-queue capable.\n"); + goto fail; + } + if (ql2xmultique_tag) { + /* create a request queue for IO */ + options |= BIT_7; + req = qla25xx_create_req_que(ha, options, 0, 0, -1, + QLA_DEFAULT_QUE_QOS); + if (!req) { + ql_log(ql_log_warn, vha, 0x00e0, + "Failed to create request queue.\n"); + goto fail; + } + ha->wq = create_workqueue("qla2xxx_wq"); + vha->req = ha->req_q_map[req]; + options |= BIT_1; + for (ques = 1; ques < ha->max_rsp_queues; ques++) { + ret = qla25xx_create_rsp_que(ha, options, 0, 0, req); + if (!ret) { + ql_log(ql_log_warn, vha, 0x00e8, + "Failed to create response queue.\n"); + goto fail2; + } + } + ha->flags.cpu_affinity_enabled = 1; + ql_dbg(ql_dbg_multiq, vha, 0xc007, + "CPU affinity mode enalbed, " + "no. of response queues:%d no. of request queues:%d.\n", + ha->max_rsp_queues, ha->max_req_queues); + ql_dbg(ql_dbg_init, vha, 0x00e9, + "CPU affinity mode enalbed, " + "no. of response queues:%d no. of request queues:%d.\n", + ha->max_rsp_queues, ha->max_req_queues); + } + return 0; +fail2: + qla25xx_delete_queues(vha); + destroy_workqueue(ha->wq); + ha->wq = NULL; + vha->req = ha->req_q_map[0]; +fail: + ha->mqenable = 0; + kfree(ha->req_q_map); + kfree(ha->rsp_q_map); + ha->max_req_queues = ha->max_rsp_queues = 1; + return 1; +} static char * -qla2x00_pci_info_str(struct scsi_qla_host *ha, char *str, int str_len) +qla2x00_pci_info_str(struct scsi_qla_host *vha, char *str, int str_len) { + struct qla_hw_data *ha = vha->hw; static char *pci_bus_modes[] = { "33", "66", "100", "133", }; @@ -303,9 +523,10 @@ qla2x00_pci_info_str(struct scsi_qla_host *ha, char *str, int str_len) } static char * -qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str, int str_len) +qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str, int str_len) { static char *pci_bus_modes[] = { "33", "66", "100", "133", }; + struct qla_hw_data *ha = vha->hw; uint32_t pci_bus; int pcie_reg; @@ -353,9 +574,10 @@ qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str, int str_len) } static char * -qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str, int str_len) +qla2x00_fw_version_str(struct scsi_qla_host *vha, char *str, int str_len) { char un_str[10]; + struct qla_hw_data *ha = vha->hw; snprintf(str, str_len, "%d.%02d.%02d ", ha->fw_major_version, ha->fw_minor_version, @@ -391,229 +613,230 @@ qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str, int str_len) } static char * -qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str, int str_len) +qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str, int str_len) { - snprintf(str, str_len, "%d.%02d.%02d ", ha->fw_major_version, - ha->fw_minor_version, - ha->fw_subminor_version); + struct qla_hw_data *ha = vha->hw; - if (ha->fw_attributes & BIT_0) - strncat(str, "[Class 2] ", str_len - (strlen(str)+1)); - if (ha->fw_attributes & BIT_1) - strncat(str, "[IP] ", str_len - (strlen(str)+1)); - if (ha->fw_attributes & BIT_2) - strncat(str, "[Multi-ID] ", str_len - (strlen(str)+1)); - if (ha->fw_attributes & BIT_3) - strncat(str, "[SB-2] ", str_len - (strlen(str)+1)); - if (ha->fw_attributes & BIT_4) - strncat(str, "[T10 CRC] ", str_len - (strlen(str)+1)); - if (ha->fw_attributes & BIT_5) - strncat(str, "[VI] ", str_len - (strlen(str)+1)); - if (ha->fw_attributes & BIT_10) - strncat(str, "[84XX] ", str_len - (strlen(str)+1)); - if (ha->fw_attributes & BIT_13) - strncat(str, "[Experimental]", str_len - (strlen(str)+1)); + snprintf(str, str_len, "%d.%02d.%02d (%x)", ha->fw_major_version, + ha->fw_minor_version, ha->fw_subminor_version, ha->fw_attributes); return str; } -static inline srb_t * -qla2x00_get_new_sp(scsi_qla_host_t *ha, fc_port_t *fcport, - struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +void +qla2x00_sp_free_dma(void *vha, void *ptr) { - srb_t *sp; + srb_t *sp = (srb_t *)ptr; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + struct qla_hw_data *ha = sp->fcport->vha->hw; + void *ctx = GET_CMD_CTX_SP(sp); - sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC); - if (!sp) - return sp; - - sp->ha = ha; - sp->fcport = fcport; - sp->cmd = cmd; - sp->flags = 0; - CMD_SP(cmd) = (void *)sp; - cmd->scsi_done = done; - - return sp; -} - -static int -qla2x00_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) -{ - scsi_qla_host_t *ha = shost_priv(cmd->device->host); - fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; - struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device)); - srb_t *sp; - int rval; - - if (unlikely(pci_channel_offline(ha->pdev))) { - cmd->result = DID_REQUEUE << 16; - goto qc_fail_command; + if (sp->flags & SRB_DMA_VALID) { + scsi_dma_unmap(cmd); + sp->flags &= ~SRB_DMA_VALID; } - rval = fc_remote_port_chkready(rport); - if (rval) { - cmd->result = rval; - goto qc_fail_command; + if (sp->flags & SRB_CRC_PROT_DMA_VALID) { + dma_unmap_sg(&ha->pdev->dev, scsi_prot_sglist(cmd), + scsi_prot_sg_count(cmd), cmd->sc_data_direction); + sp->flags &= ~SRB_CRC_PROT_DMA_VALID; } - /* Close window on fcport/rport state-transitioning. */ - if (fcport->drport) { - cmd->result = DID_IMM_RETRY << 16; - goto qc_fail_command; + if (sp->flags & SRB_CRC_CTX_DSD_VALID) { + /* List assured to be having elements */ + qla2x00_clean_dsd_pool(ha, sp); + sp->flags &= ~SRB_CRC_CTX_DSD_VALID; } - if (atomic_read(&fcport->state) != FCS_ONLINE) { - if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD || - atomic_read(&ha->loop_state) == LOOP_DEAD) { - cmd->result = DID_NO_CONNECT << 16; - goto qc_fail_command; - } - goto qc_host_busy; + if (sp->flags & SRB_CRC_CTX_DMA_VALID) { + dma_pool_free(ha->dl_dma_pool, ctx, + ((struct crc_context *)ctx)->crc_ctx_dma); + sp->flags &= ~SRB_CRC_CTX_DMA_VALID; } -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (unlikely(!qla_ini_mode_enabled(ha))) { - DEBUG2_3_11(printk("%s(%ld): Initiator command in the initiator " - "disabled mode\n", __func__, ha->host_no)); - cmd->result = DID_NO_CONNECT << 16; - goto qc_fail_command; + if (sp->flags & SRB_FCP_CMND_DMA_VALID) { + struct ct6_dsd *ctx1 = (struct ct6_dsd *)ctx; + + dma_pool_free(ha->fcp_cmnd_dma_pool, ctx1->fcp_cmnd, + ctx1->fcp_cmnd_dma); + list_splice(&ctx1->dsd_list, &ha->gbl_dsd_list); + ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt; + ha->gbl_dsd_avail += ctx1->dsd_use_cnt; + mempool_free(ctx1, ha->ctx_mempool); + ctx1 = NULL; } -#endif - - spin_unlock_irq(ha->host->host_lock); - - sp = qla2x00_get_new_sp(ha, fcport, cmd, done); - if (!sp) - goto qc_host_busy_lock; - - rval = qla2x00_start_scsi(sp); - if (rval != QLA_SUCCESS) - goto qc_host_busy_free_sp; - - spin_lock_irq(ha->host->host_lock); - - return 0; - -qc_host_busy_free_sp: - qla2x00_sp_free_dma(ha, sp); + CMD_SP(cmd) = NULL; mempool_free(sp, ha->srb_mempool); +} -qc_host_busy_lock: - spin_lock_irq(ha->host->host_lock); +static void +qla2x00_sp_compl(void *data, void *ptr, int res) +{ + struct qla_hw_data *ha = (struct qla_hw_data *)data; + srb_t *sp = (srb_t*)ptr; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); -qc_host_busy: - return SCSI_MLQUEUE_HOST_BUSY; + cmd->result = res; -qc_fail_command: - done(cmd); + if (atomic_read(&sp->ref_count) == 0) { + ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3015, + "SP reference-count to ZERO -- sp=%p cmd=%p.\n", + sp, GET_CMD_SP(sp)); + if (ql2xextended_error_logging & ql_dbg_io) + BUG(); + return; + } + if (!atomic_dec_and_test(&sp->ref_count)) + return; - return 0; + qla2x00_sp_free_dma(ha, sp); + cmd->scsi_done(cmd); } static int -qla24xx_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +qla2xxx_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) { - scsi_qla_host_t *ha = shost_priv(cmd->device->host); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device)); + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); srb_t *sp; int rval; - scsi_qla_host_t *pha = to_qla_parent(ha); - if (unlikely(pci_channel_offline(ha->pdev))) { - cmd->result = DID_REQUEUE << 16; + spin_unlock_irq(vha->host->host_lock); + if (ha->flags.eeh_busy) { + if (ha->flags.pci_channel_io_perm_failure) { + ql_dbg(ql_dbg_aer, vha, 0x9010, + "PCI Channel IO permanent failure, exiting " + "cmd=%p.\n", cmd); + cmd->result = DID_NO_CONNECT << 16; + } else { + ql_dbg(ql_dbg_aer, vha, 0x9011, + "EEH_Busy, Requeuing the cmd=%p.\n", cmd); + cmd->result = DID_REQUEUE << 16; + } goto qc24_fail_command; } rval = fc_remote_port_chkready(rport); if (rval) { cmd->result = rval; + ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3003, + "fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n", + cmd, rval); goto qc24_fail_command; } - /* Close window on fcport/rport state-transitioning. */ - if (fcport->drport) { - cmd->result = DID_IMM_RETRY << 16; + if (!vha->flags.difdix_supported && + scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) { + ql_dbg(ql_dbg_io, vha, 0x3004, + "DIF Cap not reg, fail DIF capable cmd's:%p.\n", + cmd); + cmd->result = DID_NO_CONNECT << 16; + goto qc24_fail_command; + } + + if (!fcport) { + cmd->result = DID_NO_CONNECT << 16; goto qc24_fail_command; } if (atomic_read(&fcport->state) != FCS_ONLINE) { if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD || - atomic_read(&pha->loop_state) == LOOP_DEAD) { + atomic_read(&base_vha->loop_state) == LOOP_DEAD) { + ql_dbg(ql_dbg_io, vha, 0x3005, + "Returning DNC, fcport_state=%d loop_state=%d.\n", + atomic_read(&fcport->state), + atomic_read(&base_vha->loop_state)); cmd->result = DID_NO_CONNECT << 16; goto qc24_fail_command; } - goto qc24_host_busy; + goto qc24_target_busy; } - #ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (unlikely(!qla_ini_mode_enabled(ha))) { - DEBUG2_3_11(printk("%s(%ld): Initiator command in the initiator " - "disabled mode\n", __func__, ha->host_no)); + if (unlikely(!qla_ini_mode_enabled(vha))) { + ql_dbg(ql_dbg_tgt, vha, 0xffff, + "%s(%ld): Initiator command in the initiator " + "disabled mode\n", __func__, vha->host_no); cmd->result = DID_NO_CONNECT << 16; goto qc24_fail_command; } -#endif + /* TODO: Bring in host lock changes -Arun */ +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - spin_unlock_irq(ha->host->host_lock); - - sp = qla2x00_get_new_sp(pha, fcport, cmd, done); + sp = qla2x00_get_sp(base_vha, fcport, GFP_ATOMIC); if (!sp) goto qc24_host_busy_lock; - rval = qla24xx_start_scsi(sp); - if (rval != QLA_SUCCESS) - goto qc24_host_busy_free_sp; + sp->u.scmd.cmd = cmd; + sp->type = SRB_SCSI_CMD; + atomic_set(&sp->ref_count, 1); + CMD_SP(cmd) = (void *)sp; + cmd->scsi_done = done; + sp->free = qla2x00_sp_free_dma; + sp->done = qla2x00_sp_compl; - spin_lock_irq(ha->host->host_lock); + rval = ha->isp_ops->start_scsi(sp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_io, vha, 0x3013, + "Start scsi failed rval=%d for cmd=%p.\n", rval, cmd); + goto qc24_host_busy_free_sp; + } + + spin_lock_irq(vha->host->host_lock); return 0; qc24_host_busy_free_sp: - qla2x00_sp_free_dma(pha, sp); - mempool_free(sp, pha->srb_mempool); + qla2x00_sp_free_dma(ha, sp); qc24_host_busy_lock: - spin_lock_irq(ha->host->host_lock); - -qc24_host_busy: + spin_lock_irq(vha->host->host_lock); return SCSI_MLQUEUE_HOST_BUSY; +qc24_target_busy: + spin_lock_irq(vha->host->host_lock); + return SCSI_MLQUEUE_TARGET_BUSY; + qc24_fail_command: + spin_lock_irq(vha->host->host_lock); done(cmd); return 0; } + /* * qla2x00_eh_wait_on_command * Waits for the command to be returned by the Firmware for some * max time. * * Input: - * ha = actual ha whose done queue will contain the command - * returned by firmware. * cmd = Scsi Command to wait on. - * flag = Abort/Reset(Bus or Device Reset) * * Return: * Not Found : 0 * Found : 1 */ static int -qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd) +qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd) { #define ABORT_POLLING_PERIOD 1000 #define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD)) unsigned long wait_iter = ABORT_WAIT_ITER; + scsi_qla_host_t *vha = shost_priv(cmd->device->host); + struct qla_hw_data *ha = vha->hw; int ret = QLA_SUCCESS; - while (CMD_SP(cmd)) { - msleep(ABORT_POLLING_PERIOD); + if (unlikely(pci_channel_offline(ha->pdev)) || ha->flags.eeh_busy) { + ql_dbg(ql_dbg_taskm, vha, 0x8005, + "Return:eh_wait.\n"); + return ret; + } - if (--wait_iter) - break; + while (CMD_SP(cmd) && wait_iter--) { + msleep(ABORT_POLLING_PERIOD); } if (CMD_SP(cmd)) ret = QLA_FUNCTION_FAILED; @@ -639,21 +862,22 @@ qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd) * Failed (Adapter is offline/disabled) : 1 */ int -qla2x00_wait_for_hba_online(scsi_qla_host_t *ha) +qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) { int return_status; unsigned long wait_online; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ); - while (((test_bit(ISP_ABORT_NEEDED, &pha->dpc_flags)) || - test_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags) || - test_bit(ISP_ABORT_RETRY, &pha->dpc_flags) || - pha->dpc_active) && time_before(jiffies, wait_online)) { + while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) || + test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) || + ha->dpc_active) && time_before(jiffies, wait_online)) { msleep(1000); } - if (pha->flags.online) + if (base_vha->flags.online) return_status = QLA_SUCCESS; else return_status = QLA_FUNCTION_FAILED; @@ -662,10 +886,12 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha) } EXPORT_SYMBOL(qla2x00_wait_for_hba_online); -/* - * qla2x00_wait_for_loop_ready - * Wait for MAX_LOOP_TIMEOUT(5 min) value for loop - * to be in LOOP_READY state. +/* qla2x00_wait_for_reset_ready + * Wait till the HBA is online after going through + * <= MAX_RETRIES_OF_ISP_ABORT or + * finally HBA is disabled ie marked offline or flash + * operations are in progress. + * * Input: * ha - pointer to host adapter structure * @@ -673,71 +899,73 @@ EXPORT_SYMBOL(qla2x00_wait_for_hba_online); * Does context switching-Release SPIN_LOCK * (if any) before calling this routine. * - * * Return: - * Success (LOOP_READY) : 0 - * Failed (LOOP_NOT_READY) : 1 + * Success (Adapter is online/no flash ops) : 0 + * Failed (Adapter is offline/disabled/flash ops in progress) : 1 */ -int -qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha) +static int +qla2x00_wait_for_reset_ready(scsi_qla_host_t *vha) { - int return_status = QLA_SUCCESS; - unsigned long loop_timeout ; - scsi_qla_host_t *pha = to_qla_parent(ha); + int return_status; + unsigned long wait_online; + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - /* wait for 5 min at the max for loop to be ready */ - loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ); + wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ); + while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) || + test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) || + ha->optrom_state != QLA_SWAITING || + ha->dpc_active) && time_before(jiffies, wait_online)) { - while ((!atomic_read(&pha->loop_down_timer) && - atomic_read(&pha->loop_state) == LOOP_DOWN) || - atomic_read(&pha->loop_state) != LOOP_READY) { - if (atomic_read(&pha->loop_state) == LOOP_DEAD) { - return_status = QLA_FUNCTION_FAILED; - break; - } msleep(1000); - if (time_after_eq(jiffies, loop_timeout)) { - return_status = QLA_FUNCTION_FAILED; - break; - } } - return (return_status); + if (base_vha->flags.online && ha->optrom_state == QLA_SWAITING) + return_status = QLA_SUCCESS; + else + return_status = QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_taskm, vha, 0x8019, + "%s return status=%d.\n", __func__, return_status); + + return return_status; } -void -qla2x00_abort_fcport_cmds(fc_port_t *fcport) +int +qla2x00_wait_for_chip_reset(scsi_qla_host_t *vha) { - int cnt; - unsigned long flags; - srb_t *sp; - scsi_qla_host_t *ha = fcport->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); + int return_status; + unsigned long wait_reset; + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - spin_lock_irqsave(&pha->hardware_lock, flags); - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { - sp = pha->outstanding_cmds[cnt]; - if (!sp) - continue; - if (sp->fcport != fcport) - continue; + wait_reset = jiffies + (MAX_LOOP_TIMEOUT * HZ); + while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) || + test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) || + ha->dpc_active) && time_before(jiffies, wait_reset)) { - spin_unlock_irqrestore(&pha->hardware_lock, flags); - if (ha->isp_ops->abort_command(ha, sp)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Abort failed -- %lx\n", sp->cmd->serial_number)); - } else { - if (qla2x00_eh_wait_on_command(ha, sp->cmd) != - QLA_SUCCESS) - DEBUG2(qla_printk(KERN_WARNING, ha, - "Abort failed while waiting -- %lx\n", - sp->cmd->serial_number)); + msleep(1000); - } - spin_lock_irqsave(&pha->hardware_lock, flags); + if (!test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) && + ha->flags.chip_reset_done) + break; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + if (ha->flags.chip_reset_done) + return_status = QLA_SUCCESS; + else + return_status = QLA_FUNCTION_FAILED; + + return return_status; } +static void +sp_get(struct srb *sp) +{ + atomic_inc(&sp->ref_count); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) static void qla2x00_block_error_handler(struct scsi_cmnd *cmnd) { @@ -754,6 +982,7 @@ qla2x00_block_error_handler(struct scsi_cmnd *cmnd) spin_unlock_irqrestore(shost->host_lock, flags); return; } +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) */ /************************************************************************** * qla2xxx_eh_abort @@ -773,118 +1002,123 @@ qla2x00_block_error_handler(struct scsi_cmnd *cmnd) static int qla2xxx_eh_abort(struct scsi_cmnd *cmd) { - scsi_qla_host_t *ha = shost_priv(cmd->device->host); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); srb_t *sp; - int ret, i; + int ret; unsigned int id, lun; - unsigned long serial; unsigned long flags; int wait = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); - - qla2x00_block_error_handler(cmd); + struct qla_hw_data *ha = vha->hw; if (!CMD_SP(cmd)) return SUCCESS; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) + qla2x00_block_error_handler(cmd); +#else + ret = fc_block_scsi_eh(cmd); + if (ret != SUCCESS && ret != 0) + return ret; +#endif ret = SUCCESS; id = cmd->device->id; lun = cmd->device->lun; - serial = cmd->serial_number; - /* Check active list for command command. */ - spin_lock_irqsave(&pha->hardware_lock, flags); - for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) { - sp = pha->outstanding_cmds[i]; - - if (sp == NULL) - continue; - - if (sp->cmd != cmd) - continue; - - DEBUG2(printk("%s(%ld): aborting sp %p from RISC. pid=%ld.\n", - __func__, ha->host_no, sp, serial)); - - spin_unlock_irqrestore(&pha->hardware_lock, flags); - if (ha->isp_ops->abort_command(ha, sp)) { - ret = FAILED; - DEBUG2(printk("%s(%ld): abort_command " - "mbx failed.\n", __func__, ha->host_no)); - } else { - DEBUG3(printk("%s(%ld): abort_command " - "mbx success.\n", __func__, ha->host_no)); - wait = 1; - } - spin_lock_irqsave(&pha->hardware_lock, flags); - - break; + spin_lock_irqsave(&ha->hardware_lock, flags); + sp = (srb_t *) CMD_SP(cmd); + if (!sp) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return SUCCESS; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + + ql_dbg(ql_dbg_taskm, vha, 0x8002, + "Aborting from RISC nexus=%ld:%d:%d sp=%p cmd=%p\n", + vha->host_no, id, lun, sp, cmd); + + /* Get a reference to the sp and drop the lock.*/ + sp_get(sp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (ha->isp_ops->abort_command(sp)) { + ret = FAILED; + ql_dbg(ql_dbg_taskm, vha, 0x8003, + "Abort command mbx failed cmd=%p.\n", cmd); + } else { + ql_dbg(ql_dbg_taskm, vha, 0x8004, + "Abort command mbx success cmd=%p.\n", cmd); + wait = 1; + } + spin_lock_irqsave(&ha->hardware_lock, flags); + sp->done(ha, sp, 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Did the command return during mailbox execution? */ + if (ret == FAILED && !CMD_SP(cmd)) + ret = SUCCESS; /* Wait for the command to be returned. */ if (wait) { - if (qla2x00_eh_wait_on_command(ha, cmd) != QLA_SUCCESS) { - qla_printk(KERN_ERR, ha, - "scsi(%ld:%d:%d): Abort handler timed out -- %lx " - "%x.\n", ha->host_no, id, lun, serial, ret); + if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x8006, + "Abort handler timed out cmd=%p.\n", cmd); ret = FAILED; } } - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): Abort command issued -- %d %lx %x.\n", - ha->host_no, id, lun, wait, serial, ret); + ql_log(ql_log_info, vha, 0x801c, + "Abort command issued nexus=%ld:%d:%d -- %d %x.\n", + vha->host_no, id, lun, wait, ret); return ret; } -enum nexus_wait_type { - WAIT_HOST = 0, - WAIT_TARGET, - WAIT_LUN, -}; - int -qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t, - unsigned int l, enum nexus_wait_type type) +qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, + unsigned int l, enum nexus_wait_type type) { int cnt, match, status; - srb_t *sp; unsigned long flags; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; + struct req_que *req; + srb_t *sp; + struct scsi_cmnd *cmd; status = QLA_SUCCESS; - spin_lock_irqsave(&pha->hardware_lock, flags); - for (cnt = 1; status == QLA_SUCCESS && cnt < MAX_OUTSTANDING_COMMANDS; - cnt++) { - sp = pha->outstanding_cmds[cnt]; + + spin_lock_irqsave(&ha->hardware_lock, flags); + req = vha->req; + for (cnt = 1; status == QLA_SUCCESS && + cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + sp = req->outstanding_cmds[cnt]; if (!sp) continue; - if (ha->vp_idx != sp->ha->vp_idx) + if (sp->type != SRB_SCSI_CMD) + continue; + if (vha->vp_idx != sp->fcport->vha->vp_idx) continue; match = 0; + cmd = GET_CMD_SP(sp); switch (type) { case WAIT_HOST: match = 1; break; case WAIT_TARGET: - match = sp->cmd->device->id == t; + match = cmd->device->id == t; break; case WAIT_LUN: - match = (sp->cmd->device->id == t && - sp->cmd->device->lun == l); + match = (cmd->device->id == t && + cmd->device->lun == l); break; } if (!match) continue; - spin_unlock_irqrestore(&pha->hardware_lock, flags); - status = qla2x00_eh_wait_on_command(ha, sp->cmd); - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + status = qla2x00_eh_wait_on_command(cmd); + spin_lock_irqsave(&ha->hardware_lock, flags); } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return status; } @@ -898,50 +1132,68 @@ static char *reset_errors[] = { static int __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, - struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int)) + struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int, int)) { - scsi_qla_host_t *ha = shost_priv(cmd->device->host); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; int err; - qla2x00_block_error_handler(cmd); - - if (!fcport) + if (!fcport) { return FAILED; + } - qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET ISSUED.\n", - ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) + qla2x00_block_error_handler(cmd); +#else + err = fc_block_scsi_eh(cmd); + if (err != SUCCESS && err != 0) + return err; +#endif + + ql_log(ql_log_info, vha, 0x8009, + "%s RESET ISSUED nexus=%ld:%d:%lld cmd=%p.\n", name, vha->host_no, + cmd->device->id, (u64)cmd->device->lun, cmd); err = 0; - if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) - goto eh_reset_failed; - err = 1; - if (qla2x00_wait_for_loop_ready(ha) != QLA_SUCCESS) + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x800a, + "Wait for hba online failed for cmd=%p.\n", cmd); goto eh_reset_failed; + } err = 2; - if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS) + if (do_reset(fcport, cmd->device->lun, cmd->request->cpu + 1) + != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x800c, + "do_reset failed for cmd=%p.\n", cmd); goto eh_reset_failed; + } err = 3; - if (qla2x00_eh_wait_for_pending_commands(ha, cmd->device->id, - cmd->device->lun, type) != QLA_SUCCESS) + if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id, + cmd->device->lun, type) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x800d, + "wait for peding cmds failed for cmd=%p.\n", cmd); goto eh_reset_failed; + } - qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET SUCCEEDED.\n", - ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name); + ql_log(ql_log_info, vha, 0x800e, + "%s RESET SUCCEEDED nexus:%ld:%d:%lld cmd=%p.\n", name, + vha->host_no, cmd->device->id, (u64)cmd->device->lun, cmd); return SUCCESS; - eh_reset_failed: - qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET FAILED: %s.\n", - ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name, - reset_errors[err]); +eh_reset_failed: + ql_log(ql_log_info, vha, 0x800f, + "%s RESET FAILED: %s nexus=%ld:%d:%lld cmd=%p.\n", name, + reset_errors[err], vha->host_no, cmd->device->id, (u64)cmd->device->lun, + cmd); return FAILED; } static int qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) { - scsi_qla_host_t *ha = shost_priv(cmd->device->host); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); + struct qla_hw_data *ha = vha->hw; return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd, ha->isp_ops->lun_reset); @@ -950,7 +1202,8 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) static int qla2xxx_eh_target_reset(struct scsi_cmnd *cmd) { - scsi_qla_host_t *ha = shost_priv(cmd->device->host); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); + struct qla_hw_data *ha = vha->hw; return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd, ha->isp_ops->target_reset); @@ -974,45 +1227,54 @@ qla2xxx_eh_target_reset(struct scsi_cmnd *cmd) static int qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) { - scsi_qla_host_t *ha = shost_priv(cmd->device->host); - scsi_qla_host_t *pha = to_qla_parent(ha); + scsi_qla_host_t *vha = shost_priv(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; int ret = FAILED; unsigned int id, lun; - unsigned long serial; - - qla2x00_block_error_handler(cmd); id = cmd->device->id; lun = cmd->device->lun; - serial = cmd->serial_number; - if (!fcport) + if (!fcport) { return ret; + } - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): LOOP RESET ISSUED.\n", ha->host_no, id, lun); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) + qla2x00_block_error_handler(cmd); +#else + ret = fc_block_scsi_eh(cmd); + if (ret != SUCCESS && ret != 0) + return ret; +#endif + ret = FAILED; - if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) { - DEBUG2(printk("%s failed:board disabled\n",__func__)); + ql_log(ql_log_info, vha, 0x8012, + "BUS RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); + + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x8013, + "Wait for hba online failed board disabled.\n"); goto eh_bus_reset_done; } - if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) { - if (qla2x00_loop_reset(ha) == QLA_SUCCESS) - ret = SUCCESS; - } + if (qla2x00_loop_reset(vha) == QLA_SUCCESS) + ret = SUCCESS; + if (ret == FAILED) goto eh_bus_reset_done; /* Flush outstanding commands. */ - if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) != - QLA_SUCCESS) + if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) != + QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x8014, + "Wait for pending commands failed.\n"); ret = FAILED; + } eh_bus_reset_done: - qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__, - (ret == FAILED) ? "failed" : "succeded"); + ql_log(ql_log_warn, vha, 0x802b, + "BUS RESET %s nexus=%ld:%d:%d.\n", + (ret == FAILED) ? "FAILED" : "SUCCEDED", vha->host_no, id, lun); return ret; } @@ -1035,59 +1297,61 @@ eh_bus_reset_done: static int qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) { - scsi_qla_host_t *ha = shost_priv(cmd->device->host); - fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; + scsi_qla_host_t *vha = shost_priv(cmd->device->host); + struct qla_hw_data *ha = vha->hw; int ret = FAILED; unsigned int id, lun; - unsigned long serial; - scsi_qla_host_t *pha = to_qla_parent(ha); - - qla2x00_block_error_handler(cmd); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); id = cmd->device->id; lun = cmd->device->lun; - serial = cmd->serial_number; - if (!fcport) - return ret; + ql_log(ql_log_info, vha, 0x8018, + "ADAPTER RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", ha->host_no, id, lun); - - if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) + if (qla2x00_wait_for_reset_ready(vha) != QLA_SUCCESS) goto eh_host_reset_lock; - /* - * Fixme-may be dpc thread is active and processing - * loop_resync,so wait a while for it to - * be completed and then issue big hammer.Otherwise - * it may cause I/O failure as big hammer marks the - * devices as lost kicking of the port_down_timer - * while dpc is stuck for the mailbox to complete. - */ - qla2x00_wait_for_loop_ready(ha); - set_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); - if (qla2x00_abort_isp(pha)) { - clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); - /* failed. schedule dpc to try */ - set_bit(ISP_ABORT_NEEDED, &pha->dpc_flags); - - if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) + if (vha != base_vha) { + if (qla2x00_vp_abort_isp(vha)) goto eh_host_reset_lock; - } - clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); + } else { + if (IS_QLA82XX(vha->hw)) { + if (!qla82xx_fcoe_ctx_reset(vha)) { + /* Ctx reset success */ + ret = SUCCESS; + goto eh_host_reset_lock; + } + /* fall thru if ctx reset failed */ + } + if (ha->wq) + flush_workqueue(ha->wq); - /* Waiting for our command in done_queue to be returned to OS.*/ - if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) == - QLA_SUCCESS) + set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + if (ha->isp_ops->abort_isp(base_vha)) { + clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + /* failed. schedule dpc to try */ + set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); + + if (qla2x00_wait_for_hba_online(base_vha) + != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x802a, + "wait for hba online failed.\n"); + goto eh_host_reset_lock; + } + } + clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + } + + /* Waiting for command to be returned to OS.*/ + if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) == + QLA_SUCCESS) ret = SUCCESS; - if (ha->parent) - qla2x00_vp_abort_isp(ha); - eh_host_reset_lock: - qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__, - (ret == FAILED) ? "failed" : "succeded"); + ql_log(ql_log_info, vha, 0x8017, + "ADAPTER RESET %s nexus=%ld:%d:%d.\n", + (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun); return ret; } @@ -1103,69 +1367,70 @@ eh_host_reset_lock: * 0 = success */ int -qla2x00_loop_reset(scsi_qla_host_t *ha) +qla2x00_loop_reset(scsi_qla_host_t *vha) { int ret; struct fc_port *fcport; + struct qla_hw_data *ha = vha->hw; - if (ha->flags.enable_lip_full_login) { - ret = qla2x00_full_login_lip(ha); - if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): bus_reset failed: " - "full_login_lip=%d.\n", __func__, ha->host_no, - ret)); - } - atomic_set(&ha->loop_state, LOOP_DOWN); - atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); - qla2x00_mark_all_devices_lost(ha, 0); - qla2x00_wait_for_loop_ready(ha); - } - - if (ha->flags.enable_lip_reset) { - ret = qla2x00_lip_reset(ha); - if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): bus_reset failed: " - "lip_reset=%d.\n", __func__, ha->host_no, ret)); - } - qla2x00_wait_for_loop_ready(ha); - } - - if (ha->flags.enable_target_reset) { - list_for_each_entry_rcu(fcport, &ha->fcports, list) { + if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) { + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { if (fcport->port_type != FCT_TARGET) continue; - ret = ha->isp_ops->target_reset(fcport, 0); + ret = ha->isp_ops->target_reset(fcport, 0, 0); if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): bus_reset failed: " - "target_reset=%d d_id=%x.\n", __func__, - ha->host_no, ret, fcport->d_id.b24)); + ql_dbg(ql_dbg_taskm, vha, 0x802c, + "Bus Reset failed: Target Reset=%d " + "d_id=%x.\n", ret, fcport->d_id.b24); } } } + if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) { + ret = qla2x00_full_login_lip(vha); + if (ret != QLA_SUCCESS) { + ql_dbg(ql_dbg_taskm, vha, 0x802d, + "full_login_lip=%d.\n", ret); + } + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + qla2x00_mark_all_devices_lost(vha, 0); + } + + if (ha->flags.enable_lip_reset) { + ret = qla2x00_lip_reset(vha); + if (ret != QLA_SUCCESS) + ql_dbg(ql_dbg_taskm, vha, 0x802e, + "lip_reset failed (%d).\n", ret); + } + /* Issue marker command only when we are going to start the I/O */ - ha->marker_needed = 1; + vha->marker_needed = 1; return QLA_SUCCESS; } void -qla2x00_abort_all_cmds(scsi_qla_host_t *ha, int res) +qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) { - int cnt; + int que, cnt; unsigned long flags; srb_t *sp; + struct qla_hw_data *ha = vha->hw; + struct req_que *req; spin_lock_irqsave(&ha->hardware_lock, flags); - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { - sp = ha->outstanding_cmds[cnt]; - if (sp) { - ha->outstanding_cmds[cnt] = NULL; - sp->flags = 0; - sp->cmd->result = res; - sp->cmd->host_scribble = (unsigned char *)NULL; - qla2x00_sp_compl(ha, sp); + for (que = 0; que < ha->max_req_queues; que++) { + req = ha->req_q_map[que]; + if (!req) + continue; + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + sp = req->outstanding_cmds[cnt]; + if (sp) { + req->outstanding_cmds[cnt] = NULL; + sp->done(vha, sp, res); + } } } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -1187,13 +1452,19 @@ qla2xxx_slave_alloc(struct scsi_device *sdev) static int qla2xxx_slave_configure(struct scsi_device *sdev) { - scsi_qla_host_t *ha = shost_priv(sdev->host); + scsi_qla_host_t *vha = shost_priv(sdev->host); + struct qla_hw_data *ha = vha->hw; struct fc_rport *rport = starget_to_rport(sdev->sdev_target); + struct req_que *req = vha->req; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) if (sdev->tagged_supported) - scsi_activate_tcq(sdev, ha->max_q_depth); + scsi_activate_tcq(sdev, req->max_q_depth); else - scsi_deactivate_tcq(sdev, ha->max_q_depth); + scsi_deactivate_tcq(sdev, req->max_q_depth); +#else + scsi_change_queue_depth(sdev, req->max_q_depth); +#endif rport->dev_loss_tmo = ha->port_down_retry_count; @@ -1206,7 +1477,8 @@ qla2xxx_slave_destroy(struct scsi_device *sdev) sdev->hostdata = NULL; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && \ !defined(CONFIG_SUSE_KERNEL) && \ (!defined(RHEL_RELEASE_CODE) || \ RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) @@ -1222,22 +1494,27 @@ qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth) static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth) { - scsi_qla_host_t *ha = shost_priv(sdev->host); + fc_port_t *fcport = (struct fc_port *) sdev->hostdata; if (!scsi_track_queue_full(sdev, qdepth)) return; - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%lld): Queue depth adjusted-down to %d.\n", - ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun, - sdev->queue_depth)); + ql_dbg(ql_dbg_io, fcport->vha, 0x3029, + "Queue depth adjusted-down to %d for nexus=%ld:%d:%lld.\n", + sdev->queue_depth, fcport->vha->host_no, sdev->id, (u64)sdev->lun); } static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth) { - scsi_qla_host_t *ha = shost_priv(sdev->host); + fc_port_t *fcport = sdev->hostdata; + struct scsi_qla_host *vha = fcport->vha; + struct req_que *req = NULL; - if (ha->max_q_depth <= sdev->queue_depth || ha->max_q_depth < qdepth) + req = vha->req; + if (!req) + return; + + if (req->max_q_depth <= sdev->queue_depth || req->max_q_depth < qdepth) return; if (sdev->ordered_tags) @@ -1245,10 +1522,9 @@ static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth) else scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth); - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%lld): Queue depth adjusted-up to %d.\n", - ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun, - sdev->queue_depth)); + ql_dbg(ql_dbg_io, vha, 0x302a, + "Queue depth adjusted-up to %d for nexus=%ld:%d:%lld.\n", + sdev->queue_depth, fcport->vha->host_no, sdev->id, (u64)sdev->lun); } static int @@ -1265,7 +1541,7 @@ qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) qla2x00_adjust_sdev_qdepth_up(sdev, qdepth); break; default: - return EOPNOTSUPP; + return -EOPNOTSUPP; } return sdev->queue_depth; @@ -1273,6 +1549,7 @@ qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) && (!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) static int qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type) { @@ -1287,26 +1564,27 @@ qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type) return tag_type; } +#endif /** * qla2x00_config_dma_addressing() - Configure OS DMA addressing method. * @ha: HA context * - * At exit, the @ha's flags.enable_64bit_addressing set to indicated + * At exit, the @ha's enable_64bit_addressing set to indicated * supported addressing method. */ static void -qla2x00_config_dma_addressing(scsi_qla_host_t *ha) +qla2x00_config_dma_addressing(struct qla_hw_data *ha) { /* Assume a 32bit DMA mask. */ - ha->flags.enable_64bit_addressing = 0; + ha->enable_64bit_addressing = 0; if (!dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(64))) { /* Any upper-dword bits set? */ if (MSD(dma_get_required_mask(&ha->pdev->dev)) && !pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(64))) { /* Ok, a 64bit DMA mask is applicable. */ - ha->flags.enable_64bit_addressing = 1; + ha->enable_64bit_addressing = 1; ha->isp_ops->calc_req_entries = qla2x00_calc_iocbs_64; ha->isp_ops->build_iocbs = qla2x00_build_scsi_iocbs_64; return; @@ -1318,62 +1596,260 @@ qla2x00_config_dma_addressing(scsi_qla_host_t *ha) } static void -qla2x00_enable_intrs(scsi_qla_host_t *ha) +qla2x00_enable_intrs(struct qla_hw_data *ha) { unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); ha->interrupts_on = 1; /* enable risc and host interrupts */ WRT_REG_WORD(®->ictrl, ICR_EN_INT | ICR_EN_RISC); RD_REG_WORD(®->ictrl); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); } static void -qla2x00_disable_intrs(scsi_qla_host_t *ha) +qla2x00_disable_intrs(struct qla_hw_data *ha) { unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); ha->interrupts_on = 0; /* disable risc and host interrupts */ WRT_REG_WORD(®->ictrl, 0); RD_REG_WORD(®->ictrl); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); } static void -qla24xx_enable_intrs(scsi_qla_host_t *ha) +qla24xx_enable_intrs(struct qla_hw_data *ha) { unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); ha->interrupts_on = 1; WRT_REG_DWORD(®->ictrl, ICRX_EN_RISC_INT); RD_REG_DWORD(®->ictrl); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); } static void -qla24xx_disable_intrs(scsi_qla_host_t *ha) +qla24xx_disable_intrs(struct qla_hw_data *ha) { unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - spin_lock_irqsave(&pha->hardware_lock, flags); + if (IS_NOPOLLING_TYPE(ha)) + return; + spin_lock_irqsave(&ha->hardware_lock, flags); ha->interrupts_on = 0; WRT_REG_DWORD(®->ictrl, 0); RD_REG_DWORD(®->ictrl); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static int +qla2x00_iospace_config(struct qla_hw_data *ha) +{ + resource_size_t pio; + uint16_t msix; + int cpus; + + if (pci_request_selected_regions(ha->pdev, ha->bars, + QLA2XXX_DRIVER_NAME)) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0011, + "Failed to reserve PIO/MMIO regions (%s), aborting.\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + if (!(ha->bars & 1)) + goto skip_pio; + + /* We only need PIO for Flash operations on ISP2312 v2 chips. */ + pio = pci_resource_start(ha->pdev, 0); + if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) { + if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) { + ql_log_pci(ql_log_warn, ha->pdev, 0x0012, + "Invalid pci I/O region size (%s).\n", + pci_name(ha->pdev)); + pio = 0; + } + } else { + ql_log_pci(ql_log_warn, ha->pdev, 0x0013, + "Region #0 no a PIO resource (%s).\n", + pci_name(ha->pdev)); + pio = 0; + } + ha->pio_address = pio; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0014, + "PIO address=%llu.\n", + (unsigned long long)ha->pio_address); + +skip_pio: + /* Use MMIO operations for all accesses. */ + if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0015, + "Region #1 not an MMIO resource (%s), aborting.\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + if (pci_resource_len(ha->pdev, 1) < MIN_IOBASE_LEN) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0016, + "Invalid PCI mem region size (%s), aborting.\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + ha->iobase = ioremap(pci_resource_start(ha->pdev, 1), MIN_IOBASE_LEN); + if (!ha->iobase) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0017, + "Cannot remap MMIO (%s), aborting.\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + /* Determine queue resources */ + ha->max_req_queues = ha->max_rsp_queues = 1; + if ((ql2xmaxqueues <= 1 && !ql2xmultique_tag) || + (ql2xmaxqueues > 1 && ql2xmultique_tag) || + (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))) + goto mqiobase_exit; + + ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3), + pci_resource_len(ha->pdev, 3)); + if (ha->mqiobase) { + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0018, + "MQIO Base=%p.\n", ha->mqiobase); + /* Read MSIX vector size of the board */ + pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix); + ha->msix_count = msix; + /* Max queues are bounded by available msix vectors */ + /* queue 0 uses two msix vectors */ + if (ql2xmultique_tag) { + cpus = num_online_cpus(); + ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ? + (cpus + 1) : (ha->msix_count - 1); + ha->max_req_queues = 2; + } else if (ql2xmaxqueues > 1) { + ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ? + QLA_MQ_SIZE : ql2xmaxqueues; + ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc008, + "QoS mode set, max no of request queues:%d.\n", + ha->max_req_queues); + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0019, + "QoS mode set, max no of request queues:%d.\n", + ha->max_req_queues); + } + ql_log_pci(ql_log_info, ha->pdev, 0x001a, + "MSI-X vector count: %d.\n", msix); + } else + ql_log_pci(ql_log_info, ha->pdev, 0x001b, + "BAR 3 not enabled.\n"); + +mqiobase_exit: + ha->msix_count = ha->max_rsp_queues + 1; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001c, + "MSIX Count:%d.\n", ha->msix_count); + return (0); + +iospace_error_exit: + return (-ENOMEM); +} + + +static int +qla83xx_iospace_config(struct qla_hw_data *ha) +{ + uint16_t msix; + int cpus; + + if (pci_request_selected_regions(ha->pdev, ha->bars, + QLA2XXX_DRIVER_NAME)) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0117, + "Failed to reserve PIO/MMIO regions (%s), aborting.\n", + pci_name(ha->pdev)); + + goto iospace_error_exit; + } + + /* Use MMIO operations for all accesses. */ + if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) { + ql_log_pci(ql_log_warn, ha->pdev, 0x0118, + "Invalid pci I/O region size (%s).\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) { + ql_log_pci(ql_log_warn, ha->pdev, 0x0119, + "Invalid PCI mem region size (%s), aborting\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + ha->iobase = ioremap(pci_resource_start(ha->pdev, 0), MIN_IOBASE_LEN); + if (!ha->iobase) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x011a, + "Cannot remap MMIO (%s), aborting.\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + /* 64bit PCI BAR - BAR2 will correspoond to region 4 */ + /* 83XX 26XX always use MQ type access for queues - mbar 2, a.k.a region 4 */ + ha->max_req_queues = ha->max_rsp_queues = 1; + ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 4), + pci_resource_len(ha->pdev, 4)); + + if (!ha->mqiobase) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x011d, + "BAR2/region4 not enabled\n"); + goto mqiobase_exit; + } + + ha->msixbase = ioremap(pci_resource_start(ha->pdev, 2), + pci_resource_len(ha->pdev, 2)); + if (ha->msixbase) { + /* Read MSIX vector size of the board */ + pci_read_config_word(ha->pdev, QLA_83XX_PCI_MSIX_CONTROL, &msix); + ha->msix_count = msix; + /* Max queues are bounded by available msix vectors */ + /* queue 0 uses two msix vectors */ + if (ql2xmultique_tag) { + cpus = num_online_cpus(); + ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ? + (cpus + 1) : (ha->msix_count - 1); + ha->max_req_queues = 2; + } else if (ql2xmaxqueues > 1) { + ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ? + QLA_MQ_SIZE : ql2xmaxqueues; + ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc00c, + "QoS mode set, max no of request queues:%d.\n", + ha->max_req_queues); + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b, + "QoS mode set, max no of request queues:%d.\n", + ha->max_req_queues); + } + ql_log_pci(ql_log_info, ha->pdev, 0x011c, + "MSI-X vector count: %d.\n", msix); + } else + ql_log_pci(ql_log_info, ha->pdev, 0x011e, + "BAR 1 not enabled.\n"); + +mqiobase_exit: + ha->msix_count = ha->max_rsp_queues + 1; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->msix_count += 1; /* For ATIO Q */ +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011f, + "MSIX Count:%d.\n", ha->msix_count); + return (0); + +iospace_error_exit: + return (-ENOMEM); } static struct isp_operations qla2100_isp_ops = { @@ -1408,6 +1884,9 @@ static struct isp_operations qla2100_isp_ops = { .read_optrom = qla2x00_read_optrom_data, .write_optrom = qla2x00_write_optrom_data, .get_flash_version = qla2x00_get_flash_version, + .start_scsi = qla2x00_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, }; static struct isp_operations qla2300_isp_ops = { @@ -1442,6 +1921,9 @@ static struct isp_operations qla2300_isp_ops = { .read_optrom = qla2x00_read_optrom_data, .write_optrom = qla2x00_write_optrom_data, .get_flash_version = qla2x00_get_flash_version, + .start_scsi = qla2x00_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, }; static struct isp_operations qla24xx_isp_ops = { @@ -1476,6 +1958,9 @@ static struct isp_operations qla24xx_isp_ops = { .read_optrom = qla24xx_read_optrom_data, .write_optrom = qla24xx_write_optrom_data, .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla24xx_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, }; static struct isp_operations qla25xx_isp_ops = { @@ -1510,10 +1995,124 @@ static struct isp_operations qla25xx_isp_ops = { .read_optrom = qla25xx_read_optrom_data, .write_optrom = qla24xx_write_optrom_data, .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla24xx_dif_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, +}; + +static struct isp_operations qla81xx_isp_ops = { + .pci_config = qla25xx_pci_config, + .reset_chip = qla24xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla24xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla81xx_nvram_config, + .update_fw_options = qla81xx_update_fw_options, + .load_risc = qla81xx_load_risc, + .pci_info_str = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla24xx_intr_handler, + .enable_intrs = qla24xx_enable_intrs, + .disable_intrs = qla24xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .target_reset = qla24xx_abort_target, + .lun_reset = qla24xx_lun_reset, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = NULL, + .write_nvram = NULL, + .fw_dump = qla81xx_fw_dump, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = qla83xx_beacon_blink, + .read_optrom = qla25xx_read_optrom_data, + .write_optrom = qla24xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla24xx_dif_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, +}; + +static struct isp_operations qla82xx_isp_ops = { + .pci_config = qla82xx_pci_config, + .reset_chip = qla82xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla82xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla81xx_nvram_config, + .update_fw_options = qla24xx_update_fw_options, + .load_risc = qla82xx_load_risc, + .pci_info_str = qla82xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla82xx_intr_handler, + .enable_intrs = qla82xx_enable_intrs, + .disable_intrs = qla82xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .target_reset = qla24xx_abort_target, + .lun_reset = qla24xx_lun_reset, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = qla24xx_read_nvram_data, + .write_nvram = qla24xx_write_nvram_data, + .fw_dump = qla24xx_fw_dump, + .beacon_on = qla82xx_beacon_on, + .beacon_off = qla82xx_beacon_off, + .beacon_blink = NULL, + .read_optrom = qla82xx_read_optrom_data, + .write_optrom = qla82xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla82xx_start_scsi, + .abort_isp = qla82xx_abort_isp, + .iospace_config = qla82xx_iospace_config, +}; + +static struct isp_operations qla83xx_isp_ops = { + .pci_config = qla25xx_pci_config, + .reset_chip = qla24xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla24xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla81xx_nvram_config, + .update_fw_options = qla81xx_update_fw_options, + .load_risc = qla81xx_load_risc, + .pci_info_str = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla24xx_intr_handler, + .enable_intrs = qla24xx_enable_intrs, + .disable_intrs = qla24xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .target_reset = qla24xx_abort_target, + .lun_reset = qla24xx_lun_reset, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = NULL, + .write_nvram = NULL, + .fw_dump = qla83xx_fw_dump, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = qla83xx_beacon_blink, + .read_optrom = qla25xx_read_optrom_data, + .write_optrom = qla24xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla24xx_dif_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla83xx_iospace_config, }; static inline void -qla2x00_set_isp_flags(scsi_qla_host_t *ha) +qla2x00_set_isp_flags(struct qla_hw_data *ha) { ha->device_type = DT_EXTENDED_IDS; switch (ha->pdev->device) { @@ -1591,155 +2190,148 @@ qla2x00_set_isp_flags(scsi_qla_host_t *ha) ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; - } -} - -static int -qla2x00_iospace_config(scsi_qla_host_t *ha) -{ - resource_size_t pio; - - if (pci_request_selected_regions(ha->pdev, ha->bars, - QLA2XXX_DRIVER_NAME)) { - qla_printk(KERN_WARNING, ha, - "Failed to reserve PIO/MMIO regions (%s)\n", - pci_name(ha->pdev)); - - goto iospace_error_exit; - } - if (!(ha->bars & 1)) - goto skip_pio; - - /* We only need PIO for Flash operations on ISP2312 v2 chips. */ - pio = pci_resource_start(ha->pdev, 0); - if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) { - if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) { - qla_printk(KERN_WARNING, ha, - "Invalid PCI I/O region size (%s)...\n", - pci_name(ha->pdev)); - pio = 0; - } - } else { - qla_printk(KERN_WARNING, ha, - "region #0 not a PIO resource (%s)...\n", - pci_name(ha->pdev)); - pio = 0; - } - ha->pio_address = pio; - -skip_pio: - /* Use MMIO operations for all accesses. */ - if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) { - qla_printk(KERN_ERR, ha, - "region #1 not an MMIO resource (%s), aborting\n", - pci_name(ha->pdev)); - goto iospace_error_exit; - } - if (pci_resource_len(ha->pdev, 1) < MIN_IOBASE_LEN) { - qla_printk(KERN_ERR, ha, - "Invalid PCI mem region size (%s), aborting\n", - pci_name(ha->pdev)); - goto iospace_error_exit; + case PCI_DEVICE_ID_QLOGIC_ISP8001: + ha->device_type |= DT_ISP8001; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; + case PCI_DEVICE_ID_QLOGIC_ISP8021: + ha->device_type |= DT_ISP8021; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + /* Initialize 82XX ISP flags */ + qla82xx_init_flags(ha); + break; + case PCI_DEVICE_ID_QLOGIC_ISP2031: + ha->device_type |= DT_ISP2031; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; + ha->device_type |= DT_T10_PI; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; + case PCI_DEVICE_ID_QLOGIC_ISP8031: + ha->device_type |= DT_ISP8031; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; + ha->device_type |= DT_T10_PI; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; } - ha->iobase = ioremap(pci_resource_start(ha->pdev, 1), MIN_IOBASE_LEN); - if (!ha->iobase) { - qla_printk(KERN_ERR, ha, - "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev)); + if (IS_QLA82XX(ha)) + ha->port_no = !(ha->portnum & 1); + else + /* Get adapter physical port no from interrupt pin register. */ + pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no); - goto iospace_error_exit; - } - - return (0); - -iospace_error_exit: - return (-ENOMEM); + if (ha->port_no & 1) + ha->flags.port0 = 1; + else + ha->flags.port0 = 0; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b, + "device_type=0x%x port=%d fw_srisc_address=0x%x.\n", + ha->device_type, ha->flags.port0, ha->fw_srisc_address); } static void qla2xxx_scan_start(struct Scsi_Host *shost) { - scsi_qla_host_t *ha = shost_priv(shost); + scsi_qla_host_t *vha = shost_priv(shost); - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); - set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); - set_bit(RSCN_UPDATE, &ha->dpc_flags); + if (vha->hw->flags.running_gold_fw) + return; + + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + set_bit(RSCN_UPDATE, &vha->dpc_flags); + set_bit(NPIV_CONFIG_NEEDED, &vha->dpc_flags); } static int qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time) { - scsi_qla_host_t *ha = shost_priv(shost); + scsi_qla_host_t *vha = shost_priv(shost); - if (!ha->host) + if (!vha->host) return 1; - if (time > ha->loop_reset_delay * HZ) + if (time > vha->hw->loop_reset_delay * HZ) return 1; - return atomic_read(&ha->loop_state) == LOOP_READY; + return atomic_read(&vha->loop_state) == LOOP_READY; } +/* + * PCI driver interface + */ #ifdef CONFIG_SCSI_QLA2XXX_TARGET void qla2xxx_add_targets(void) { - scsi_qla_host_t *ha; + struct qla_hw_data *ha; + + if (qla_target.tgt_host_action == NULL) + goto out; mutex_lock(&qla_ha_list_mutex); list_for_each_entry(ha, &qla_ha_list, ha_list_entry) { - if (qla_target.tgt_host_action != NULL) { - int i, vp_idx_matched; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + int i; - qla_target.tgt_host_action(ha, ADD_TARGET); - for_each_mapped_vp_idx(ha, i) { - scsi_qla_host_t *vha; - vp_idx_matched = 0; + qla_target.tgt_host_action(base_vha, ADD_TARGET); - list_for_each_entry(vha, &ha->vp_list, - vp_list) { - if (i == vha->vp_idx) { - vp_idx_matched = 1; - break; - } - } - - if (vp_idx_matched) + for_each_mapped_vp_idx(base_vha, i) { + scsi_qla_host_t *vha; + list_for_each_entry(vha, &base_vha->hw->vp_list, list) { + if (i == vha->vp_idx) { qla_target.tgt_host_action(vha, ADD_TARGET); + break; + } } } } mutex_unlock(&qla_ha_list_mutex); + +out: return; } EXPORT_SYMBOL(qla2xxx_add_targets); #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED)) -size_t qla2xxx_add_vtarget(u64 *port_name, u64 *node_name, u64 *parent_host) +size_t qla2xxx_add_vtarget(u64 port_name, u64 node_name, u64 parent_host) { struct Scsi_Host *shost = NULL; - scsi_qla_host_t *ha; + struct qla_hw_data *ha; struct fc_vport_identifiers vid; uint8_t parent_wwn[WWN_SIZE]; memset(&vid, 0, sizeof(vid)); - u64_to_wwn(*parent_host, parent_wwn); + u64_to_wwn(parent_host, parent_wwn); mutex_lock(&qla_ha_list_mutex); list_for_each_entry(ha, &qla_ha_list, ha_list_entry) { - if (!memcmp(parent_wwn, ha->port_name, WWN_SIZE)) { - shost = ha->host; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + if (!memcmp(parent_wwn, + base_vha->port_name_set ? base_vha->hw->orig_hw_port_name : + base_vha->port_name, + WWN_SIZE)) { + shost = base_vha->host; break; } } mutex_unlock(&qla_ha_list_mutex); - if (!ha || !shost) + if (!shost) return -ENODEV; - vid.port_name = *port_name; - vid.node_name = *node_name; - vid.roles = FC_PORT_ROLE_FCP_INITIATOR; + vid.port_name = port_name; + vid.node_name = node_name; + vid.roles = FC_PORT_ROLE_FCP_TARGET; vid.vport_type = FC_PORTTYPE_NPIV; /* vid.symbolic_name is already zero/NULL's */ vid.disable = false; /* always enabled */ @@ -1752,9 +2344,9 @@ size_t qla2xxx_add_vtarget(u64 *port_name, u64 *node_name, u64 *parent_host) } EXPORT_SYMBOL(qla2xxx_add_vtarget); -size_t qla2xxx_del_vtarget(u64 *port_name) +size_t qla2xxx_del_vtarget(u64 port_name) { - scsi_qla_host_t *ha; + struct qla_hw_data *ha; struct Scsi_Host *shost; struct fc_host_attrs *fc_host; struct fc_vport *vport; @@ -1763,13 +2355,14 @@ size_t qla2xxx_del_vtarget(u64 *port_name) mutex_lock(&qla_ha_list_mutex); list_for_each_entry(ha, &qla_ha_list, ha_list_entry) { - shost = ha->host; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + shost = base_vha->host; fc_host = shost_to_fc_host(shost); spin_lock_irqsave(shost->host_lock, flags); /* We only allow support on Channel 0 !!! */ list_for_each_entry(vport, &fc_host->vports, peers) { if ((vport->channel == 0) && - (vport->port_name == *port_name)) { + (vport->port_name == port_name)) { match = 1; break; } @@ -1786,18 +2379,23 @@ size_t qla2xxx_del_vtarget(u64 *port_name) return fc_vport_terminate(vport); } EXPORT_SYMBOL(qla2xxx_del_vtarget); -#else +#else #warning "Patch scst_fc_vport_create was not applied on\ your kernel. Adding NPIV targets using SCST sysfs interface will be disabled." #endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \ defined(FC_VPORT_CREATE_DEFINED))*/ +void qla_unknown_atio_work_fn(struct delayed_work *work) +{ + struct qla_hw_data *ha = container_of(work, struct qla_hw_data, + unknown_atio_work); + qla_target.tgt_try_to_dequeue_unknown_atios(ha); + return; +} + #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ -/* - * PCI driver interface - */ static int #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) __devinit @@ -1806,24 +2404,35 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { int ret = -ENODEV; struct Scsi_Host *host; - scsi_qla_host_t *ha; + scsi_qla_host_t *base_vha = NULL; + struct qla_hw_data *ha; char pci_info[30]; - char fw_str[128]; + char fw_str[30]; struct scsi_host_template *sht; int bars, mem_only = 0; + uint16_t req_length = 0, rsp_length = 0; + struct req_que *req = NULL; + struct rsp_que *rsp = NULL; bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO); - sht = &qla2x00_driver_template; + sht = &qla2xxx_driver_template; if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 || - pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532) { + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2031 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8031) { bars = pci_select_bars(pdev, IORESOURCE_MEM); - sht = &qla24xx_driver_template; mem_only = 1; + ql_dbg_pci(ql_dbg_init, pdev, 0x0007, + "Mem only adapter.\n"); } + ql_dbg_pci(ql_dbg_init, pdev, 0x0008, + "Bars=%d.\n", bars); if (mem_only) { if (pci_enable_device_mem(pdev)) @@ -1833,232 +2442,443 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) goto probe_out; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) - if (pci_find_aer_capability(pdev)) - if (pci_enable_pcie_error_reporting(pdev)) - goto probe_out; -#else /* taken from 2.6.28 */ /* This may fail but that's ok */ pci_enable_pcie_error_reporting(pdev); -#endif - host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t)); - if (host == NULL) { - printk(KERN_WARNING - "qla2xxx: Couldn't allocate host from scsi layer!\n"); - goto probe_disable_device; + ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL); + if (!ha) { + ql_log_pci(ql_log_fatal, pdev, 0x0009, + "Unable to allocate memory for ha.\n"); + goto probe_out; } + ql_dbg_pci(ql_dbg_init, pdev, 0x000a, + "Memory allocated for ha=%p.\n", ha); + ha->pdev = pdev; /* Clear our data area */ - ha = shost_priv(host); - memset(ha, 0, sizeof(scsi_qla_host_t)); - - ha->enable_class_2 = ql2xenableclass2; - ha->pdev = pdev; - ha->host = host; - ha->host_no = host->host_no; - snprintf(ha->host_str, HA_HOST_STR_SIZE, "%s_%ld", QLA2XXX_DRIVER_NAME, - ha->host_no); - ha->parent = NULL; ha->bars = bars; ha->mem_only = mem_only; - spin_lock_init(&ha->hardware_lock); - -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - mutex_init(&ha->tgt_mutex); - mutex_init(&ha->tgt_host_action_mutex); - qla_clear_tgt_mode(ha); - ha->tgt_vp_map = NULL; -#endif + spin_lock_init(&ha->vport_slock); + spin_lock_init(&ha->dpc_lock); /* Set ISP-type information. */ qla2x00_set_isp_flags(ha); - /* Configure PCI I/O space */ - ret = qla2x00_iospace_config(ha); - if (ret) - goto probe_failed; - - qla_printk(KERN_INFO, ha, - "Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq, - ha->iobase); + /* Set EEH reset type to fundamental if required by hba */ + if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha)) + pdev->needs_freset = 1; ha->prev_topology = 0; ha->init_cb_size = sizeof(init_cb_t); - ha->mgmt_svr_loop_id = MANAGEMENT_SERVER + ha->vp_idx; ha->link_data_rate = PORT_SPEED_UNKNOWN; ha->optrom_size = OPTROM_SIZE_2300; - ha->max_q_depth = MAX_Q_DEPTH; - if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU) - ha->max_q_depth = ql2xmaxqdepth; - /* Assign ISP specific operations. */ if (IS_QLA2100(ha)) { - host->max_id = MAX_TARGETS_2100; + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100; ha->mbx_count = MAILBOX_REGISTER_COUNT_2100; - ha->request_q_length = REQUEST_ENTRY_CNT_2100; - ha->response_q_length = RESPONSE_ENTRY_CNT_2100; - ha->last_loop_id = SNS_LAST_LOOP_ID_2100; - host->sg_tablesize = 32; + req_length = REQUEST_ENTRY_CNT_2100; + rsp_length = RESPONSE_ENTRY_CNT_2100; + ha->max_loop_id = SNS_LAST_LOOP_ID_2100; ha->gid_list_info_size = 4; + ha->flash_conf_off = ~0; + ha->flash_data_off = ~0; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA2200(ha)) { - host->max_id = MAX_TARGETS_2200; - ha->mbx_count = MAILBOX_REGISTER_COUNT; - ha->request_q_length = REQUEST_ENTRY_CNT_2200; - ha->response_q_length = RESPONSE_ENTRY_CNT_2100; - ha->last_loop_id = SNS_LAST_LOOP_ID_2100; + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100; + ha->mbx_count = MAILBOX_REGISTER_COUNT_2200; + req_length = REQUEST_ENTRY_CNT_2200; + rsp_length = RESPONSE_ENTRY_CNT_2100; + ha->max_loop_id = SNS_LAST_LOOP_ID_2100; ha->gid_list_info_size = 4; + ha->flash_conf_off = ~0; + ha->flash_data_off = ~0; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA23XX(ha)) { - host->max_id = MAX_TARGETS_2200; + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100; ha->mbx_count = MAILBOX_REGISTER_COUNT; - ha->request_q_length = REQUEST_ENTRY_CNT_2200; - ha->response_q_length = RESPONSE_ENTRY_CNT_2300; - ha->last_loop_id = SNS_LAST_LOOP_ID_2300; + req_length = REQUEST_ENTRY_CNT_2200; + rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->gid_list_info_size = 6; if (IS_QLA2322(ha) || IS_QLA6322(ha)) ha->optrom_size = OPTROM_SIZE_2322; + ha->flash_conf_off = ~0; + ha->flash_data_off = ~0; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; ha->isp_ops = &qla2300_isp_ops; } else if (IS_QLA24XX_TYPE(ha)) { - host->max_id = MAX_TARGETS_2200; + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; - ha->request_q_length = REQUEST_ENTRY_CNT_24XX; - ha->response_q_length = RESPONSE_ENTRY_CNT_2300; + req_length = REQUEST_ENTRY_CNT_24XX; + rsp_length = RESPONSE_ENTRY_CNT_24XX; #ifdef CONFIG_SCSI_QLA2XXX_TARGET ha->atio_q_length = ATIO_ENTRY_CNT_24XX; -#endif - ha->last_loop_id = SNS_LAST_LOOP_ID_2300; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_24xx); - ha->mgmt_svr_loop_id = 10 + ha->vp_idx; ha->gid_list_info_size = 8; ha->optrom_size = OPTROM_SIZE_24XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA24XX; ha->isp_ops = &qla24xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA; + ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; + ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; } else if (IS_QLA25XX(ha)) { - host->max_id = MAX_TARGETS_2200; + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; - ha->request_q_length = REQUEST_ENTRY_CNT_24XX; - ha->response_q_length = RESPONSE_ENTRY_CNT_2300; + req_length = REQUEST_ENTRY_CNT_24XX; + rsp_length = RESPONSE_ENTRY_CNT_24XX; #ifdef CONFIG_SCSI_QLA2XXX_TARGET ha->atio_q_length = ATIO_ENTRY_CNT_24XX; -#endif - ha->last_loop_id = SNS_LAST_LOOP_ID_2300; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_24xx); - ha->mgmt_svr_loop_id = 10 + ha->vp_idx; ha->gid_list_info_size = 8; ha->optrom_size = OPTROM_SIZE_25XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; ha->isp_ops = &qla25xx_isp_ops; - ha->hw_event_start = PCI_FUNC(pdev->devfn) ? - FA_HW_EVENT1_ADDR: FA_HW_EVENT0_ADDR; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA; + ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; + ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; + } else if (IS_QLA81XX(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_24XX; + rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; + ha->init_cb_size = sizeof(struct mid_init_cb_81xx); + ha->gid_list_info_size = 8; + ha->optrom_size = OPTROM_SIZE_81XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; + ha->isp_ops = &qla81xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; + } else if (IS_QLA82XX(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_82XX; + rsp_length = RESPONSE_ENTRY_CNT_82XX; + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; + ha->init_cb_size = sizeof(struct mid_init_cb_81xx); + ha->gid_list_info_size = 8; + ha->optrom_size = OPTROM_SIZE_82XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; + ha->isp_ops = &qla82xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA; + ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; + ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; + } else if (IS_QLA83XX(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_24XX; + rsp_length = RESPONSE_ENTRY_CNT_24XX; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->atio_q_length = ATIO_ENTRY_CNT_24XX; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; + ha->init_cb_size = sizeof(struct mid_init_cb_81xx); + ha->gid_list_info_size = 8; + ha->optrom_size = OPTROM_SIZE_83XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; + ha->isp_ops = &qla83xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; } - host->can_queue = ha->request_q_length + 128; - /* load the F/W, read paramaters, and init the H/W */ - ha->instance = num_hosts; + ql_dbg_pci(ql_dbg_init, pdev, 0x001e, + "mbx_count=%d, req_length=%d, " + "rsp_length=%d, max_loop_id=%d, init_cb_size=%d, " + "gid_list_info_size=%d, optrom_size=%d, nvram_npiv_size=%d, " + "max_fibre_devices=%d.\n", + ha->mbx_count, req_length, rsp_length, ha->max_loop_id, + ha->init_cb_size, ha->gid_list_info_size, ha->optrom_size, + ha->nvram_npiv_size, ha->max_fibre_devices); + ql_dbg_pci(ql_dbg_init, pdev, 0x001f, + "isp_ops=%p, flash_conf_off=%d, " + "flash_data_off=%d, nvram_conf_off=%d, nvram_data_off=%d.\n", + ha->isp_ops, ha->flash_conf_off, ha->flash_data_off, + ha->nvram_conf_off, ha->nvram_data_off); - spin_lock_init(&ha->dpc_lock); + /* Configure PCI I/O space */ + ret = ha->isp_ops->iospace_config(ha); + if (ret) + goto probe_hw_failed; + ql_log_pci(ql_log_info, pdev, 0x001d, + "Found an ISP%04X irq %d iobase 0x%p.\n", + pdev->device, pdev->irq, ha->iobase); mutex_init(&ha->vport_lock); init_completion(&ha->mbx_cmd_comp); complete(&ha->mbx_cmd_comp); init_completion(&ha->mbx_intr_comp); - init_completion(&ha->pass_thru_intr_comp); - - INIT_LIST_HEAD(&ha->list); - INIT_LIST_HEAD(&ha->fcports); - INIT_LIST_HEAD(&ha->vp_list); - INIT_LIST_HEAD(&ha->work_list); - - set_bit(0, (unsigned long *) ha->vp_idx_map); + init_completion(&ha->dcbx_comp); qla2x00_config_dma_addressing(ha); - if (qla2x00_mem_alloc(ha)) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to allocate memory for adapter\n"); + ql_dbg_pci(ql_dbg_init, pdev, 0x0020, + "64 Bit addressing is %s.\n", + ha->enable_64bit_addressing ? "enable" : + "disable"); + ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp); + if (!ret) { + ql_log_pci(ql_log_fatal, pdev, 0x0031, + "Failed to allocate memory for adapter, aborting.\n"); - ret = -ENOMEM; - goto probe_failed; + goto probe_hw_failed; } - if (qla2x00_initialize_adapter(ha)) { - qla_printk(KERN_WARNING, ha, - "Failed to initialize adapter\n"); + req->max_q_depth = MAX_Q_DEPTH; + if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU) + req->max_q_depth = ql2xmaxqdepth; - DEBUG2(printk("scsi(%ld): Failed to initialize adapter - " - "Adapter flags %x.\n", - ha->host_no, ha->device_flags)); + + base_vha = qla2x00_create_host(sht, ha); + if (!base_vha) { + ret = -ENOMEM; + qla2x00_mem_free(ha); + qla2x00_free_req_que(ha, req); + qla2x00_free_rsp_que(ha, rsp); + goto probe_hw_failed; + } + + pci_set_drvdata(pdev, base_vha); + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + set_bit(0, (unsigned long *)ha->vp_idx_map); + mutex_init(&base_vha->tgt_mutex); + mutex_init(&base_vha->tgt_host_action_mutex); + INIT_LIST_HEAD(&ha->unknown_atio_list); + INIT_DELAYED_WORK(&ha->unknown_atio_work, + (void (*)(struct work_struct *))qla_unknown_atio_work_fn); + qla_clear_tgt_mode(base_vha); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + + host = base_vha->host; + base_vha->req = req; + host->can_queue = req->length + 128; + if (IS_QLA2XXX_MIDTYPE(ha)) + base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx; + else + base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER + + base_vha->vp_idx; + + /* Set the SG table size based on ISP type */ + if (!IS_FWI2_CAPABLE(ha)) { + if (IS_QLA2100(ha)) + host->sg_tablesize = 32; + } else { + if (!IS_QLA82XX(ha)) + host->sg_tablesize = QLA_SG_ALL; + } + ql_dbg(ql_dbg_init, base_vha, 0x0032, + "can_queue=%d, req=%p, " + "mgmt_svr_loop_id=%d, sg_tablesize=%d.\n", + host->can_queue, base_vha->req, + base_vha->mgmt_svr_loop_id, host->sg_tablesize); + host->max_id = ha->max_fibre_devices; + host->this_id = 255; + host->cmd_per_lun = 3; + host->unique_id = host->host_no; + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) + host->max_cmd_len = 32; + else + host->max_cmd_len = MAX_CMDSZ; + host->max_channel = MAX_BUSES - 1; + host->max_lun = ql2xmaxlun; + host->transportt = qla2xxx_transport_template; + sht->vendor_id = (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC); + + ql_dbg(ql_dbg_init, base_vha, 0x0033, + "max_id=%d this_id=%d " + "cmd_per_len=%d unique_id=%d max_cmd_len=%d max_channel=%d " + "max_lun=%lld transportt=%p, vendor_id=%llu.\n", host->max_id, + host->this_id, host->cmd_per_lun, host->unique_id, + host->max_cmd_len, host->max_channel, (u64)host->max_lun, + host->transportt, sht->vendor_id); + +que_init: + /* Alloc arrays of request and response ring ptrs */ + if (!qla2x00_alloc_queues(ha, req, rsp)) { + ql_log(ql_log_fatal, base_vha, 0x003d, + "Failed to allocate memory for queue pointers..." + "aborting.\n"); + goto probe_init_failed; + } + + + /* Set up the irqs */ + ret = qla2x00_request_irqs(ha, rsp); + if (ret) + goto probe_init_failed; + + pci_save_state(pdev); + + /* Assign back pointers */ + rsp->req = req; + req->rsp = rsp; + + /* FWI2-capable only. */ + req->req_q_in = &ha->iobase->isp24.req_q_in; + req->req_q_out = &ha->iobase->isp24.req_q_out; + rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in; + rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out; + if (ha->mqenable || IS_QLA83XX(ha)) { + req->req_q_in = &ha->mqiobase->isp25mq.req_q_in; + req->req_q_out = &ha->mqiobase->isp25mq.req_q_out; + rsp->rsp_q_in = &ha->mqiobase->isp25mq.rsp_q_in; + rsp->rsp_q_out = &ha->mqiobase->isp25mq.rsp_q_out; + } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (ha->mqenable || IS_QLA83XX(ha)) { + ISP_ATIO_Q_IN(base_vha) = &ha->mqiobase->isp25mq.atio_q_in; + ISP_ATIO_Q_OUT(base_vha) = &ha->mqiobase->isp25mq.atio_q_out; + } else { + ISP_ATIO_Q_IN(base_vha) = &ha->iobase->isp24.atio_q_in; + ISP_ATIO_Q_OUT(base_vha) = &ha->iobase->isp24.atio_q_out; + } +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + + if (IS_QLA82XX(ha)) { + req->req_q_out = &ha->iobase->isp82.req_q_out[0]; + rsp->rsp_q_in = &ha->iobase->isp82.rsp_q_in[0]; + rsp->rsp_q_out = &ha->iobase->isp82.rsp_q_out[0]; + } + + ql_dbg(ql_dbg_multiq, base_vha, 0xc009, + "rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n", + ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp); + ql_dbg(ql_dbg_multiq, base_vha, 0xc00a, + "req->req_q_in=%p req->req_q_out=%p " + "rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n", + req->req_q_in, req->req_q_out, + rsp->rsp_q_in, rsp->rsp_q_out); + ql_dbg(ql_dbg_init, base_vha, 0x003e, + "rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n", + ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp); + ql_dbg(ql_dbg_init, base_vha, 0x003f, + "req->req_q_in=%p req->req_q_out=%p rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n", + req->req_q_in, req->req_q_out, rsp->rsp_q_in, rsp->rsp_q_out); + + if (qla2x00_initialize_adapter(base_vha)) { + ql_log(ql_log_fatal, base_vha, 0x00d6, + "Failed to initialize adapter - Adapter flags %x.\n", + base_vha->device_flags); + + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_FAILED); + qla82xx_idc_unlock(ha); + ql_log(ql_log_fatal, base_vha, 0x00d7, + "HW State: FAILED.\n"); + } ret = -ENODEV; goto probe_failed; } + if (ha->mqenable) { + if (qla25xx_setup_mode(base_vha)) { + ql_log(ql_log_warn, base_vha, 0x00ec, + "Failed to create queues, falling back to single queue mode.\n"); + goto que_init; + } + } + + if (ha->flags.running_gold_fw) + goto skip_dpc; + /* * Startup the kernel thread for this host adapter */ ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha, - "%s_dpc", ha->host_str); + "%s_dpc", base_vha->host_str); if (IS_ERR(ha->dpc_thread)) { - qla_printk(KERN_WARNING, ha, - "Unable to start DPC thread!\n"); + ql_log(ql_log_fatal, base_vha, 0x00ed, + "Failed to start DPC thread.\n"); ret = PTR_ERR(ha->dpc_thread); goto probe_failed; } + ql_dbg(ql_dbg_init, base_vha, 0x00ee, + "DPC thread started successfully.\n"); - host->this_id = 255; - host->cmd_per_lun = 3; - host->unique_id = ha->instance; - host->max_cmd_len = MAX_CMDSZ; - host->max_channel = MAX_BUSES - 1; - host->max_lun = MAX_LUNS; - host->transportt = qla2xxx_transport_template; - - ret = qla2x00_request_irqs(ha); - if (ret) - goto probe_failed; +skip_dpc: + list_add_tail(&base_vha->list, &ha->vp_list); + base_vha->host->irq = ha->pdev->irq; /* Initialized the timer */ - qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL); + qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL); + ql_dbg(ql_dbg_init, base_vha, 0x00ef, + "Started qla2x00_timer with " + "interval=%d.\n", WATCH_INTERVAL); + ql_dbg(ql_dbg_init, base_vha, 0x00f0, + "Detected hba at address=%p.\n", + ha); - DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n", - ha->host_no, ha)); + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) { + if (ha->fw_attributes & BIT_4) { + int prot = 0; + base_vha->flags.difdix_supported = 1; + ql_dbg(ql_dbg_init, base_vha, 0x00f1, + "Registering for DIF/DIX type 1 and 3 protection.\n"); + if (ql2xenabledif == 1) + prot = SHOST_DIX_TYPE0_PROTECTION; + scsi_host_set_prot(host, + prot | SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE2_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION + | SHOST_DIX_TYPE1_PROTECTION + | SHOST_DIX_TYPE2_PROTECTION + | SHOST_DIX_TYPE3_PROTECTION); + scsi_host_set_guard(host, SHOST_DIX_GUARD_CRC); + } else + base_vha->flags.difdix_supported = 0; + } - pci_set_drvdata(pdev, ha); - - ha->flags.init_done = 1; - ha->flags.online = 1; - - num_hosts++; + ha->isp_ops->enable_intrs(ha); ret = scsi_add_host(host, &pdev->dev); if (ret) goto probe_failed; - ha->isp_ops->enable_intrs(ha); + base_vha->flags.init_done = 1; + base_vha->flags.online = 1; + + ql_dbg(ql_dbg_init, base_vha, 0x00f2, + "Init done and hba is online.\n"); scsi_scan_host(host); - qla2x00_alloc_sysfs_attr(ha); + qla2x00_alloc_sysfs_attr(base_vha); - qla2x00_init_host_attr(ha); + qla2x00_init_host_attr(base_vha); - qla2x00_dfs_setup(ha); - - qla_printk(KERN_INFO, ha, "\n" - " QLogic Fibre Channel HBA Driver: %s\n" - " QLogic %s - %s\n" - " ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n", - qla2x00_version_str, ha->model_number, - ha->model_desc ? ha->model_desc: "", pdev->device, - ha->isp_ops->pci_info_str(ha, pci_info, sizeof(pci_info)), - pci_name(pdev), ha->flags.enable_64bit_addressing ? '+': '-', - ha->host_no, ha->isp_ops->fw_version_str(ha, fw_str, - sizeof(fw_str))); + qla2x00_dfs_setup(base_vha); + ql_log(ql_log_info, base_vha, 0x00fb, + "QLogic %s - %s.\n", ha->model_number, ha->model_desc); + ql_log(ql_log_info, base_vha, 0x00fc, + "ISP%04X: %s @ %s hdma%c host#=%ld fw=%s.\n", + pdev->device, ha->isp_ops->pci_info_str(base_vha, pci_info, sizeof(pci_info)), + pci_name(pdev), ha->enable_64bit_addressing ? '+' : '-', + base_vha->host_no, + ha->isp_ops->fw_version_str(base_vha, fw_str, sizeof(fw_str))); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_host_action != NULL) - qla_target.tgt_host_action(ha, ADD_TARGET); + qla_target.tgt_host_action(base_vha, ADD_TARGET); /* * Must be after tgt_host_action() to not race with @@ -2067,48 +2887,123 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) mutex_lock(&qla_ha_list_mutex); list_add_tail(&ha->ha_list_entry, &qla_ha_list); mutex_unlock(&qla_ha_list_mutex); -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ return 0; +probe_init_failed: + qla2x00_free_req_que(ha, req); + ha->req_q_map[0] = NULL; + clear_bit(0, ha->req_qid_map); + qla2x00_free_rsp_que(ha, rsp); + ha->rsp_q_map[0] = NULL; + clear_bit(0, ha->rsp_qid_map); + ha->max_req_queues = ha->max_rsp_queues = 0; + probe_failed: - qla2x00_free_device(ha); + if (base_vha->timer_active) + qla2x00_stop_timer(base_vha); + base_vha->flags.online = 0; - scsi_host_put(host); + qla2x00_free_device(base_vha); -probe_disable_device: - pci_disable_device(pdev); + scsi_host_put(base_vha->host); + +probe_hw_failed: + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + iounmap((device_reg_t __iomem *)ha->nx_pcibase); + if (!ql2xdbwr) + iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr); + } else { + if (ha->iobase) + iounmap(ha->iobase); + } + pci_release_selected_regions(ha->pdev, ha->bars); + kfree(ha); + ha = NULL; probe_out: + pci_disable_device(pdev); return ret; } static void -qla2x00_stop_dpc_thread(scsi_qla_host_t *ha) +qla2x00_shutdown(struct pci_dev *pdev) { - struct task_struct *t = NULL; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; - spin_lock_irq(&ha->dpc_lock); - if (ha->dpc_thread != NULL) { - t = ha->dpc_thread; - /* - * qla2xxx_wake_dpc checks for ->dpc_thread - * so we need to zero it out. - */ - ha->dpc_thread = NULL; + vha = pci_get_drvdata(pdev); + ha = vha->hw; + + /* Turn-off FCE trace */ + if (ha->flags.fce_enabled) { + qla2x00_disable_fce_trace(vha, NULL, NULL); + ha->flags.fce_enabled = 0; } - spin_unlock_irq(&ha->dpc_lock); - if (t != NULL) - kthread_stop(t); + /* Turn-off EFT trace */ + if (ha->eft) + qla2x00_disable_eft_trace(vha); + + /* Stop currently executing firmware. */ + qla2x00_try_to_stop_firmware(vha); + + /* Turn adapter off line */ + vha->flags.online = 0; + + /* turn-off interrupts on the card */ + if (ha->interrupts_on) { + vha->flags.init_done = 0; + ha->isp_ops->disable_intrs(ha); + } + + qla2x00_free_irqs(vha); + + qla2x00_free_fw_dump(ha); } + +static void +qla2x00_stop_dpc_thread(struct qla_hw_data *ha) +{ + struct task_struct *t = NULL; + + spin_lock_irq(&ha->dpc_lock); + if (ha->dpc_thread != NULL) { + t = ha->dpc_thread; + /* + * qla2xxx_wake_dpc checks for ->dpc_thread + * so we need to zero it out. + */ + ha->dpc_thread = NULL; + } + spin_unlock_irq(&ha->dpc_lock); + + if (t != NULL) + kthread_stop(t); +} + + static void qla2x00_remove_one(struct pci_dev *pdev) { - scsi_qla_host_t *ha; + scsi_qla_host_t *base_vha, *vha; + struct qla_hw_data *ha; + unsigned long flags; - ha = pci_get_drvdata(pdev); + /* + * If the PCI device is disabled that means that probe failed and any + * resources should be have cleaned up on probe exit. + */ + if (!atomic_read(&pdev->enable_cnt)) + return; + + base_vha = pci_get_drvdata(pdev); + ha = base_vha->hw; #ifdef CONFIG_SCSI_QLA2XXX_TARGET /* @@ -2119,75 +3014,154 @@ qla2x00_remove_one(struct pci_dev *pdev) list_del(&ha->ha_list_entry); mutex_unlock(&qla_ha_list_mutex); - ha->host_shutting_down = 1; + base_vha->hw->host_shutting_down = 1; if (qla_target.tgt_host_action != NULL) - qla_target.tgt_host_action(ha, REMOVE_TARGET); -#endif + qla_target.tgt_host_action(base_vha, REMOVE_TARGET); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - /* Necessary to prevent races with it */ - qla2x00_stop_dpc_thread(ha); + mutex_lock(&ha->vport_lock); + while (ha->cur_vport_count) { + spin_lock_irqsave(&ha->vport_slock, flags); - qla2x00_dfs_remove(ha); + BUG_ON(base_vha->list.next == &ha->vp_list); + /* This assumes first entry in ha->vp_list is always base vha */ + vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list); + scsi_host_get(vha->host); - qla84xx_put_chip(ha); + spin_unlock_irqrestore(&ha->vport_slock, flags); + mutex_unlock(&ha->vport_lock); - qla2x00_free_sysfs_attr(ha); + fc_vport_terminate(vha->fc_vport); + scsi_host_put(vha->host); - fc_remove_host(ha->host); + mutex_lock(&ha->vport_lock); + } + mutex_unlock(&ha->vport_lock); - scsi_remove_host(ha->host); + set_bit(UNLOADING, &base_vha->dpc_flags); - qla2x00_free_device(ha); + qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); - scsi_host_put(ha->host); + qla2x00_dfs_remove(base_vha); + qla84xx_put_chip(base_vha); + + /* Disable timer */ + if (base_vha->timer_active) + qla2x00_stop_timer(base_vha); + + base_vha->flags.online = 0; + + /* Flush the work queue and remove it */ + if (ha->wq) { + flush_workqueue(ha->wq); + destroy_workqueue(ha->wq); + ha->wq = NULL; + } + + /* Necessary to prevent races with it */ + qla2x00_stop_dpc_thread(ha); + + qla2x00_free_sysfs_attr(base_vha); + + fc_remove_host(base_vha->host); + + scsi_remove_host(base_vha->host); + + qla2x00_free_device(base_vha); + + scsi_host_put(base_vha->host); + + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + + iounmap((device_reg_t __iomem *)ha->nx_pcibase); + if (!ql2xdbwr) + iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr); + } else { + if (ha->iobase) + iounmap(ha->iobase); + + if (ha->mqiobase) + iounmap(ha->mqiobase); + + if (IS_QLA83XX(ha) && ha->msixbase) + iounmap(ha->msixbase); + } + + pci_release_selected_regions(ha->pdev, ha->bars); + kfree(ha); + ha = NULL; + + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } static void -qla2x00_free_device(scsi_qla_host_t *ha) +qla2x00_free_device(scsi_qla_host_t *vha) { - qla2x00_abort_all_cmds(ha, DID_NO_CONNECT << 16); + struct qla_hw_data *ha = vha->hw; + + qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16); /* Disable timer */ - if (ha->timer_active) - qla2x00_stop_timer(ha); - - ha->flags.online = 0; + if (vha->timer_active) + qla2x00_stop_timer(vha); /* Kill the kernel thread for this host */ qla2x00_stop_dpc_thread(ha); + qla25xx_delete_queues(vha); + if (ha->flags.fce_enabled) - qla2x00_disable_fce_trace(ha, NULL, NULL); + qla2x00_disable_fce_trace(vha, NULL, NULL); if (ha->eft) - qla2x00_disable_eft_trace(ha); + qla2x00_disable_eft_trace(vha); /* Stop currently executing firmware. */ - qla2x00_try_to_stop_firmware(ha); + qla2x00_try_to_stop_firmware(vha); + + vha->flags.online = 0; /* turn-off interrupts on the card */ - if (ha->interrupts_on) + if (ha->interrupts_on) { + vha->flags.init_done = 0; ha->isp_ops->disable_intrs(ha); + } + + qla2x00_free_irqs(vha); + + qla2x00_free_fcports(vha); qla2x00_mem_free(ha); - qla2x00_free_irqs(ha); + qla82xx_md_free(vha); - /* release io space registers */ - if (ha->iobase) - iounmap(ha->iobase); - pci_release_selected_regions(ha->pdev, ha->bars); + qla2x00_free_queues(ha); +} + +void qla2x00_free_fcports(struct scsi_qla_host *vha) +{ + fc_port_t *fcport, *tfcport; + + list_for_each_entry_safe(fcport, tfcport, &vha->vp_fcports, list) { + list_del(&fcport->list); + kfree(fcport); + fcport = NULL; + } } static inline void -qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport, +qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, int defer) { struct fc_rport *rport; + scsi_qla_host_t *base_vha; unsigned long flags; if (!fcport->rport) @@ -2195,17 +3169,18 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport, rport = fcport->rport; if (defer) { - spin_lock_irqsave(ha->host->host_lock, flags); + base_vha = pci_get_drvdata(vha->hw->pdev); + spin_lock_irqsave(vha->host->host_lock, flags); fcport->drport = rport; - spin_unlock_irqrestore(ha->host->host_lock, flags); - set_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); + spin_unlock_irqrestore(vha->host->host_lock, flags); + set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags); + qla2xxx_wake_dpc(base_vha); } else { fc_remote_port_delete(rport); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_fc_port_deleted) - qla_target.tgt_fc_port_deleted(ha, fcport); -#endif + qla_target.tgt_fc_port_deleted(vha, fcport); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ } } @@ -2218,41 +3193,37 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport, * * Context: */ -void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport, +void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, int do_login, int defer) { if (atomic_read(&fcport->state) == FCS_ONLINE && - ha->vp_idx == fcport->vp_idx) - qla2x00_schedule_rport_del(ha, fcport, defer); - + vha->vp_idx == fcport->vha->vp_idx) { + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); + qla2x00_schedule_rport_del(vha, fcport, defer); + } /* * We may need to retry the login, so don't change the state of the * port but do the retries. */ if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD) - atomic_set(&fcport->state, FCS_DEVICE_LOST); + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); if (!do_login) return; if (fcport->login_retry == 0) { - fcport->login_retry = ha->login_retry_count; - set_bit(RELOGIN_NEEDED, &ha->dpc_flags); + fcport->login_retry = vha->hw->login_retry_count; + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - DEBUG(printk("scsi(%ld): Port login retry: " + ql_dbg(ql_dbg_disc, vha, 0x2067, + "Port login retry " "%02x%02x%02x%02x%02x%02x%02x%02x, " - "id = 0x%04x retry cnt=%d\n", - ha->host_no, - fcport->port_name[0], - fcport->port_name[1], - fcport->port_name[2], - fcport->port_name[3], - fcport->port_name[4], - fcport->port_name[5], - fcport->port_name[6], - fcport->port_name[7], - fcport->loop_id, - fcport->login_retry)); + "id = 0x%04x retry cnt=%d.\n", + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7], + fcport->loop_id, fcport->login_retry); } } @@ -2270,14 +3241,14 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport, * Context: */ void -qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) +qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer) { fc_port_t *fcport; - scsi_qla_host_t *pha = to_qla_parent(ha); - list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (ha->vp_idx != 0 && ha->vp_idx != fcport->vp_idx) + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { + if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx) continue; + /* * No point in marking the device as lost, if the device is * already DEAD. @@ -2285,12 +3256,12 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD) continue; if (atomic_read(&fcport->state) == FCS_ONLINE) { + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); if (defer) - qla2x00_schedule_rport_del(ha, fcport, defer); - else if (ha->vp_idx == fcport->vp_idx) - qla2x00_schedule_rport_del(ha, fcport, defer); + qla2x00_schedule_rport_del(vha, fcport, defer); + else if (vha->vp_idx == fcport->vha->vp_idx) + qla2x00_schedule_rport_del(vha, fcport, defer); } - atomic_set(&fcport->state, FCS_DEVICE_LOST); } } @@ -2303,28 +3274,22 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) * !0 = failure. */ static int -qla2x00_mem_alloc(scsi_qla_host_t *ha) +qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, + struct req_que **req, struct rsp_que **rsp) { char name[16]; - ha->request_ring = dma_alloc_coherent(&ha->pdev->dev, - (ha->request_q_length + 1) * sizeof(request_t), &ha->request_dma, - GFP_KERNEL); - if (!ha->request_ring) + ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size, + &ha->init_cb_dma, GFP_KERNEL); + if (!ha->init_cb) goto fail; - ha->response_ring = dma_alloc_coherent(&ha->pdev->dev, - (ha->response_q_length + 1) * sizeof(response_t), - &ha->response_dma, GFP_KERNEL); - if (!ha->response_ring) - goto fail_free_request_ring; - #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (IS_FWI2_CAPABLE(ha)) { ha->tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) * MAX_MULTI_ID_FABRIC, GFP_KERNEL); if (!ha->tgt_vp_map) - goto fail_free_response_ring; + goto fail_free_init_cb; ha->atio_ring = dma_alloc_coherent(&ha->pdev->dev, (ha->atio_q_length + 1) * sizeof(atio_t), @@ -2332,95 +3297,238 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha) if (ha->atio_ring == NULL) goto fail_free_vp_map; } -#endif - - ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, - &ha->gid_list_dma, GFP_KERNEL); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, + qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL); if (!ha->gid_list) +#ifdef CONFIG_SCSI_QLA2XXX_TARGET goto fail_free_atio_ring; - - ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size, - &ha->init_cb_dma, GFP_KERNEL); - if (!ha->init_cb) - goto fail_free_gid_list; - - snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME, - ha->host_no); - ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev, - DMA_POOL_SIZE, 8, 0); - if (!ha->s_dma_pool) +#else /* CONFIG_SCSI_QLA2XXX_TARGET */ goto fail_free_init_cb; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep); if (!ha->srb_mempool) - goto fail_free_s_dma_pool; + goto fail_free_gid_list; + + if (IS_QLA82XX(ha)) { + /* Allocate cache for CT6 Ctx. */ + if (!ctx_cachep) { + ctx_cachep = kmem_cache_create("qla2xxx_ctx", + sizeof(struct ct6_dsd), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!ctx_cachep) + goto fail_free_gid_list; + } + ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ, + ctx_cachep); + if (!ha->ctx_mempool) + goto fail_free_srb_mempool; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0021, + "ctx_cachep=%p ctx_mempool=%p.\n", + ctx_cachep, ha->ctx_mempool); + } /* Get memory for cached NVRAM */ ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL); if (!ha->nvram) - goto fail_free_srb_mempool; + goto fail_free_ctx_mempool; + + snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME, + ha->pdev->device); + ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev, + DMA_POOL_SIZE, 8, 0); + if (!ha->s_dma_pool) + goto fail_free_nvram; + + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0022, + "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n", + ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool); + + if (IS_QLA82XX(ha) || ql2xenabledif) { + ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev, + DSD_LIST_DMA_POOL_SIZE, 8, 0); + if (!ha->dl_dma_pool) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0023, + "Failed to allocate memory for dl_dma_pool.\n"); + goto fail_s_dma_pool; + } + + ha->fcp_cmnd_dma_pool = dma_pool_create(name, &ha->pdev->dev, + FCP_CMND_DMA_POOL_SIZE, 8, 0); + if (!ha->fcp_cmnd_dma_pool) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0024, + "Failed to allocate memory for fcp_cmnd_dma_pool.\n"); + goto fail_dl_dma_pool; + } + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0025, + "dl_dma_pool=%p fcp_cmnd_dma_pool=%p.\n", + ha->dl_dma_pool, ha->fcp_cmnd_dma_pool); + } /* Allocate memory for SNS commands */ if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - /* Get consistent memory allocated for SNS commands */ + /* Get consistent memory allocated for SNS commands */ ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev, - sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL); + sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL); if (!ha->sns_cmd) - goto fail_free_nvram; + goto fail_dma_pool; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0026, + "sns_cmd: %p.\n", ha->sns_cmd); } else { - /* Get consistent memory allocated for MS IOCB */ + /* Get consistent memory allocated for MS IOCB */ ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, - &ha->ms_iocb_dma); + &ha->ms_iocb_dma); if (!ha->ms_iocb) - goto fail_free_nvram; - - /* Get consistent memory allocated for CT SNS commands */ + goto fail_dma_pool; + /* Get consistent memory allocated for CT SNS commands */ ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev, - sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL); + sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL); if (!ha->ct_sns) goto fail_free_ms_iocb; - - /* Get consistent memory allocated for pass-thru commands */ - ha->pass_thru = dma_alloc_coherent(&ha->pdev->dev, - PAGE_SIZE, &ha->pass_thru_dma, GFP_KERNEL); - if (!ha->pass_thru) { - qla_printk(KERN_WARNING, ha, - "Memory Allocation failed - pass_thru\n"); - goto fail_free_pass_thru; - } + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0027, + "ms_iocb=%p ct_sns=%p.\n", + ha->ms_iocb, ha->ct_sns); } - return 0; + /* Allocate memory for request ring */ + *req = kzalloc(sizeof(struct req_que), GFP_KERNEL); + if (!*req) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0028, + "Failed to allocate memory for req.\n"); + goto fail_req; + } + (*req)->length = req_len; + (*req)->ring = dma_alloc_coherent(&ha->pdev->dev, + ((*req)->length + 1) * sizeof(request_t), + &(*req)->dma, GFP_KERNEL); + if (!(*req)->ring) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0029, + "Failed to allocate memory for req_ring.\n"); + goto fail_req_ring; + } + /* Allocate memory for response ring */ + *rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL); + if (!*rsp) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x002a, + "Failed to allocate memory for rsp.\n"); + goto fail_rsp; + } + (*rsp)->hw = ha; + (*rsp)->length = rsp_len; + (*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev, + ((*rsp)->length + 1) * sizeof(response_t), + &(*rsp)->dma, GFP_KERNEL); + if (!(*rsp)->ring) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x002b, + "Failed to allocate memory for rsp_ring.\n"); + goto fail_rsp_ring; + } + (*req)->rsp = *rsp; + (*rsp)->req = *req; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002c, + "req=%p req->length=%d req->ring=%p rsp=%p " + "rsp->length=%d rsp->ring=%p.\n", + *req, (*req)->length, (*req)->ring, *rsp, (*rsp)->length, + (*rsp)->ring); + /* Allocate memory for NVRAM data for vports */ + if (ha->nvram_npiv_size) { + ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) * + ha->nvram_npiv_size, GFP_KERNEL); + if (!ha->npiv_info) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x002d, + "Failed to allocate memory for npiv_info.\n"); + goto fail_npiv_info; + } + } else + ha->npiv_info = NULL; -fail_free_pass_thru: - dma_free_coherent(&ha->pdev->dev, - PAGE_SIZE, ha->pass_thru, ha->pass_thru_dma); - ha->pass_thru = NULL; + /* Get consistent memory allocated for EX-INIT-CB. */ + if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) { + ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, + &ha->ex_init_cb_dma); + if (!ha->ex_init_cb) + goto fail_ex_init_cb; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002e, + "ex_init_cb=%p.\n", ha->ex_init_cb); + } + + /* Get consistent memory allocated for Async Port-Database. */ + if (!IS_FWI2_CAPABLE(ha)) { + ha->async_pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, + &ha->async_pd_dma); + if (!ha->async_pd) + goto fail_async_pd; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002f, + "async_pd=%p.\n", ha->async_pd); + } + + INIT_LIST_HEAD(&ha->gbl_dsd_list); + INIT_LIST_HEAD(&ha->vp_list); + return 1; + +fail_async_pd: + dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); +fail_ex_init_cb: + kfree(ha->npiv_info); +fail_npiv_info: + dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) * + sizeof(response_t), (*rsp)->ring, (*rsp)->dma); + (*rsp)->ring = NULL; + (*rsp)->dma = 0; +fail_rsp_ring: + kfree(*rsp); +fail_rsp: + dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) * + sizeof(request_t), (*req)->ring, (*req)->dma); + (*req)->ring = NULL; + (*req)->dma = 0; +fail_req_ring: + kfree(*req); +fail_req: + dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt), + ha->ct_sns, ha->ct_sns_dma); + ha->ct_sns = NULL; + ha->ct_sns_dma = 0; fail_free_ms_iocb: dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); ha->ms_iocb = NULL; ha->ms_iocb_dma = 0; +fail_dma_pool: + if (IS_QLA82XX(ha) || ql2xenabledif) { + dma_pool_destroy(ha->fcp_cmnd_dma_pool); + ha->fcp_cmnd_dma_pool = NULL; + } +fail_dl_dma_pool: + if (IS_QLA82XX(ha) || ql2xenabledif) { + dma_pool_destroy(ha->dl_dma_pool); + ha->dl_dma_pool = NULL; + } +fail_s_dma_pool: + dma_pool_destroy(ha->s_dma_pool); + ha->s_dma_pool = NULL; fail_free_nvram: kfree(ha->nvram); ha->nvram = NULL; +fail_free_ctx_mempool: + mempool_destroy(ha->ctx_mempool); + ha->ctx_mempool = NULL; fail_free_srb_mempool: mempool_destroy(ha->srb_mempool); ha->srb_mempool = NULL; -fail_free_s_dma_pool: - dma_pool_destroy(ha->s_dma_pool); - ha->s_dma_pool = NULL; -fail_free_init_cb: - dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb, - ha->init_cb_dma); - ha->init_cb = NULL; - ha->init_cb_dma = 0; fail_free_gid_list: - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, - ha->gid_list_dma); + dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), + ha->gid_list, + ha->gid_list_dma); ha->gid_list = NULL; ha->gid_list_dma = 0; -fail_free_atio_ring: +fail_free_init_cb: + dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb, + ha->init_cb_dma); + ha->init_cb = NULL; + ha->init_cb_dma = 0; #ifdef CONFIG_SCSI_QLA2XXX_TARGET +fail_free_atio_ring: dma_free_coherent(&ha->pdev->dev, (ha->atio_q_length + 1) * sizeof(response_t), ha->atio_ring, ha->atio_dma); ha->atio_ring = NULL; @@ -2428,37 +3536,23 @@ fail_free_atio_ring: fail_free_vp_map: kfree(ha->tgt_vp_map); ha->tgt_vp_map = NULL; -fail_free_response_ring: -#endif - dma_free_coherent(&ha->pdev->dev, (ha->response_q_length + 1) * - sizeof(response_t), ha->response_ring, ha->response_dma); - ha->response_ring = NULL; - ha->response_dma = 0; -fail_free_request_ring: - dma_free_coherent(&ha->pdev->dev, (ha->request_q_length + 1) * - sizeof(request_t), ha->request_ring, ha->request_dma); - ha->request_ring = NULL; - ha->request_dma = 0; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ fail: + ql_log(ql_log_fatal, NULL, 0x0030, + "Memory allocation failure.\n"); return -ENOMEM; } /* -* qla2x00_mem_free -* Frees all adapter allocated memory. +* qla2x00_free_fw_dump +* Frees fw dump stuff. * * Input: -* ha = adapter block pointer. +* ha = adapter block pointer. */ static void -qla2x00_mem_free(scsi_qla_host_t *ha) +qla2x00_free_fw_dump(struct qla_hw_data *ha) { - struct list_head *fcpl, *fcptemp; - fc_port_t *fcport; - - if (ha->srb_mempool) - mempool_destroy(ha->srb_mempool); - if (ha->fce) dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma); @@ -2469,14 +3563,45 @@ qla2x00_mem_free(scsi_qla_host_t *ha) ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma); vfree(ha->fw_dump); } + ha->fce = NULL; + ha->fce_dma = 0; + ha->eft = NULL; + ha->eft_dma = 0; + ha->fw_dump = NULL; + ha->fw_dumped = 0; + ha->fw_dump_reading = 0; +} + +/* +* qla2x00_mem_free +* Frees all adapter allocated memory. +* +* Input: +* ha = adapter block pointer. +*/ +static void +qla2x00_mem_free(struct qla_hw_data *ha) +{ + qla2x00_free_fw_dump(ha); + + if (ha->srb_mempool) + mempool_destroy(ha->srb_mempool); + + if (ha->dcbx_tlv) + dma_free_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE, + ha->dcbx_tlv, ha->dcbx_tlv_dma); + + if (ha->xgmac_data) + dma_free_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE, + ha->xgmac_data, ha->xgmac_data_dma); if (ha->sns_cmd) dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt), - ha->sns_cmd, ha->sns_cmd_dma); + ha->sns_cmd, ha->sns_cmd_dma); if (ha->ct_sns) dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt), - ha->ct_sns, ha->ct_sns_dma); + ha->ct_sns, ha->ct_sns_dma); if (ha->sfp_data) dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma); @@ -2484,16 +3609,19 @@ qla2x00_mem_free(scsi_qla_host_t *ha) if (ha->ms_iocb) dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); + if (ha->ex_init_cb) + dma_pool_free(ha->s_dma_pool, + ha->ex_init_cb, ha->ex_init_cb_dma); + + if (ha->async_pd) + dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma); + if (ha->s_dma_pool) dma_pool_destroy(ha->s_dma_pool); - if (ha->init_cb) - dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, - ha->init_cb, ha->init_cb_dma); - if (ha->gid_list) - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, - ha->gid_list_dma); + dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), + ha->gid_list, ha->gid_list_dma); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (ha->atio_ring) @@ -2501,25 +3629,43 @@ qla2x00_mem_free(scsi_qla_host_t *ha) (ha->atio_q_length + 1) * sizeof(atio_t), ha->atio_ring, ha->atio_dma); kfree(ha->tgt_vp_map); -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - if (ha->response_ring) - dma_free_coherent(&ha->pdev->dev, - (ha->response_q_length + 1) * sizeof(response_t), - ha->response_ring, ha->response_dma); + if (IS_QLA82XX(ha)) { + if (!list_empty(&ha->gbl_dsd_list)) { + struct dsd_dma *dsd_ptr, *tdsd_ptr; - if (ha->request_ring) - dma_free_coherent(&ha->pdev->dev, - (ha->request_q_length + 1) * sizeof(request_t), - ha->request_ring, ha->request_dma); + /* clean up allocated prev pool */ + list_for_each_entry_safe(dsd_ptr, + tdsd_ptr, &ha->gbl_dsd_list, list) { + dma_pool_free(ha->dl_dma_pool, + dsd_ptr->dsd_addr, dsd_ptr->dsd_list_dma); + list_del(&dsd_ptr->list); + kfree(dsd_ptr); + } + } + } - if (ha->pass_thru) - dma_free_coherent(&ha->pdev->dev, - PAGE_SIZE, ha->pass_thru, ha->pass_thru_dma); + if (ha->dl_dma_pool) + dma_pool_destroy(ha->dl_dma_pool); + + if (ha->fcp_cmnd_dma_pool) + dma_pool_destroy(ha->fcp_cmnd_dma_pool); + + if (ha->ctx_mempool) + mempool_destroy(ha->ctx_mempool); + + if (ha->init_cb) + dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, + ha->init_cb, ha->init_cb_dma); + + vfree(ha->optrom_buffer); + kfree(ha->nvram); + kfree(ha->npiv_info); + kfree(ha->swl); ha->srb_mempool = NULL; - ha->eft = NULL; - ha->eft_dma = 0; + ha->ctx_mempool = NULL; ha->sns_cmd = NULL; ha->sns_cmd_dma = 0; ha->ct_sns = NULL; @@ -2528,52 +3674,81 @@ qla2x00_mem_free(scsi_qla_host_t *ha) ha->ms_iocb_dma = 0; ha->init_cb = NULL; ha->init_cb_dma = 0; + ha->ex_init_cb = NULL; + ha->ex_init_cb_dma = 0; + ha->async_pd = NULL; + ha->async_pd_dma = 0; ha->s_dma_pool = NULL; + ha->dl_dma_pool = NULL; + ha->fcp_cmnd_dma_pool = NULL; - ha->gid_list = NULL; - ha->gid_list_dma = 0; - - ha->pass_thru = NULL; - - ha->response_ring = NULL; - ha->response_dma = 0; - ha->request_ring = NULL; - ha->request_dma = 0; #ifdef CONFIG_SCSI_QLA2XXX_TARGET ha->atio_ring = NULL; ha->atio_dma = 0; ha->tgt_vp_map = NULL; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - list_for_each_safe(fcpl, fcptemp, &ha->fcports) { - fcport = list_entry(fcpl, fc_port_t, list); + ha->gid_list = NULL; + ha->gid_list_dma = 0; +} - /* fc ports */ - list_del_init(&fcport->list); - kfree(fcport); +struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, + struct qla_hw_data *ha) +{ + struct Scsi_Host *host; + struct scsi_qla_host *vha = NULL; + + host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t)); + if (host == NULL) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0107, + "Failed to allocate host from the scsi layer, aborting.\n"); + goto fail; } - INIT_LIST_HEAD(&ha->fcports); - ha->fw_dump = NULL; - ha->fw_dumped = 0; - ha->fw_dump_reading = 0; + /* Clear our data area */ + vha = shost_priv(host); + memset(vha, 0, sizeof(scsi_qla_host_t)); - vfree(ha->optrom_buffer); - kfree(ha->nvram); + vha->host = host; + vha->host_no = host->host_no; + vha->hw = ha; + + INIT_LIST_HEAD(&vha->vp_fcports); + INIT_LIST_HEAD(&vha->work_list); + INIT_LIST_HEAD(&vha->list); + + spin_lock_init(&vha->work_lock); + + snprintf(vha->host_str, sizeof(vha->host_str), "%s_%ld", + QLA2XXX_DRIVER_NAME, vha->host_no); + ql_dbg(ql_dbg_init, vha, 0x0041, + "Allocated the host=%p hw=%p vha=%p dev_name=%s", + vha->host, vha->hw, vha, + dev_name(&(ha->pdev->dev))); + + return vha; + +fail: + return vha; } static struct qla_work_evt * -qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type, - int locked) +qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type) { struct qla_work_evt *e; + uint8_t bail; - e = kzalloc(sizeof(struct qla_work_evt), locked ? GFP_ATOMIC: - GFP_KERNEL); - if (!e) + QLA_VHA_MARK_BUSY(vha, bail); + if (bail) return NULL; + e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC); + if (!e) { + QLA_VHA_MARK_NOT_BUSY(vha); + return NULL; + } + INIT_LIST_HEAD(&e->list); e->type = type; e->flags = QLA_EVT_FLAG_FREE; @@ -2581,79 +3756,260 @@ qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type, } static int -qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked) +qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e) { - unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); + unsigned long flags; + + spin_lock_irqsave(&vha->work_lock, flags); + list_add_tail(&e->list, &vha->work_list); + spin_unlock_irqrestore(&vha->work_lock, flags); + qla2xxx_wake_dpc(vha); - if (!locked) - spin_lock_irqsave(&pha->hardware_lock, flags); - list_add_tail(&e->list, &ha->work_list); - qla2xxx_wake_dpc(ha); - if (!locked) - spin_unlock_irqrestore(&pha->hardware_lock, flags); return QLA_SUCCESS; } int -qla2x00_post_aen_work(struct scsi_qla_host *ha, enum fc_host_event_code code, +qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code, u32 data) { struct qla_work_evt *e; - e = qla2x00_alloc_work(ha, QLA_EVT_AEN, 1); + e = qla2x00_alloc_work(vha, QLA_EVT_AEN); if (!e) return QLA_FUNCTION_FAILED; e->u.aen.code = code; e->u.aen.data = data; - return qla2x00_post_work(ha, e, 1); + return qla2x00_post_work(vha, e); } int -qla2x00_post_hwe_work(struct scsi_qla_host *ha, uint16_t code, uint16_t d1, - uint16_t d2, uint16_t d3) +qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb) { struct qla_work_evt *e; - e = qla2x00_alloc_work(ha, QLA_EVT_HWE_LOG, 1); + e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK); if (!e) return QLA_FUNCTION_FAILED; - e->u.hwe.code = code; - e->u.hwe.d1 = d1; - e->u.hwe.d2 = d2; - e->u.hwe.d3 = d3; - return qla2x00_post_work(ha, e, 1); + memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); + return qla2x00_post_work(vha, e); +} + +#define qla2x00_post_async_work(name, type) \ +int qla2x00_post_async_##name##_work( \ + struct scsi_qla_host *vha, \ + fc_port_t *fcport, uint16_t *data) \ +{ \ + struct qla_work_evt *e; \ + \ + e = qla2x00_alloc_work(vha, type); \ + if (!e) \ + return QLA_FUNCTION_FAILED; \ + \ + e->u.logio.fcport = fcport; \ + if (data) { \ + e->u.logio.data[0] = data[0]; \ + e->u.logio.data[1] = data[1]; \ + } \ + return qla2x00_post_work(vha, e); \ +} + +qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN); +qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE); +qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT); +qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE); +qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC); +qla2x00_post_async_work(adisc_done, QLA_EVT_ASYNC_ADISC_DONE); + +int +qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.uevent.code = code; + return qla2x00_post_work(vha, e); } static void -qla2x00_do_work(struct scsi_qla_host *ha) +qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code) { - struct qla_work_evt *e; - scsi_qla_host_t *pha = to_qla_parent(ha); + char event_string[40]; + char *envp[] = { event_string, NULL }; - spin_lock_irq(&pha->hardware_lock); - while (!list_empty(&ha->work_list)) { - e = list_first_entry(&ha->work_list, struct qla_work_evt, list); + switch (code) { + case QLA_UEVENT_CODE_FW_DUMP: + snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld", + vha->host_no); + break; + default: + /* do nothing */ + break; + } + kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp); +} + +void +qla2x00_do_work(struct scsi_qla_host *vha) +{ + struct qla_work_evt *e, *tmp; + unsigned long flags; + LIST_HEAD(work); + + spin_lock_irqsave(&vha->work_lock, flags); + list_splice_init(&vha->work_list, &work); + spin_unlock_irqrestore(&vha->work_lock, flags); + + list_for_each_entry_safe(e, tmp, &work, list) { list_del_init(&e->list); - spin_unlock_irq(&pha->hardware_lock); switch (e->type) { case QLA_EVT_AEN: - fc_host_post_event(ha->host, fc_get_event_number(), + fc_host_post_event(vha->host, fc_get_event_number(), e->u.aen.code, e->u.aen.data); break; - case QLA_EVT_HWE_LOG: - qla2xxx_hw_event_log(ha, e->u.hwe.code, e->u.hwe.d1, - e->u.hwe.d2, e->u.hwe.d3); + case QLA_EVT_IDC_ACK: + qla81xx_idc_ack(vha, e->u.idc_ack.mb); + break; + case QLA_EVT_ASYNC_LOGIN: + qla2x00_async_login(vha, e->u.logio.fcport, + e->u.logio.data); + break; + case QLA_EVT_ASYNC_LOGIN_DONE: + qla2x00_async_login_done(vha, e->u.logio.fcport, + e->u.logio.data); + break; + case QLA_EVT_ASYNC_LOGOUT: + qla2x00_async_logout(vha, e->u.logio.fcport); + break; + case QLA_EVT_ASYNC_LOGOUT_DONE: + qla2x00_async_logout_done(vha, e->u.logio.fcport, + e->u.logio.data); + break; + case QLA_EVT_ASYNC_ADISC: + qla2x00_async_adisc(vha, e->u.logio.fcport, + e->u.logio.data); + break; + case QLA_EVT_ASYNC_ADISC_DONE: + qla2x00_async_adisc_done(vha, e->u.logio.fcport, + e->u.logio.data); + break; + case QLA_EVT_UEVENT: + qla2x00_uevent_emit(vha, e->u.uevent.code); break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); - spin_lock_irq(&pha->hardware_lock); + + /* For each work completed decrement vha ref count */ + QLA_VHA_MARK_NOT_BUSY(vha); } - spin_unlock_irq(&pha->hardware_lock); +} + +/* Relogins all the fcports of a vport + * Context: dpc thread + */ +void qla2x00_relogin(struct scsi_qla_host *vha) +{ + fc_port_t *fcport; + int status; + uint16_t next_loopid = 0; + struct qla_hw_data *ha = vha->hw; + uint16_t data[2]; + + if (!qla_ini_mode_enabled(vha)) + goto out; + + list_for_each_entry_rcu(fcport, &vha->vp_fcports, list) { + /* + * If the port is not ONLINE then try to login + * to it if we haven't run out of retries. + */ + if (atomic_read(&fcport->state) != FCS_ONLINE && + fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) { + fcport->login_retry--; + if (fcport->flags & FCF_FABRIC_DEVICE) { + if (fcport->flags & FCF_FCP2_DEVICE) + ha->isp_ops->fabric_logout(vha, + fcport->loop_id, + fcport->d_id.b.domain, + fcport->d_id.b.area, + fcport->d_id.b.al_pa); + + if (fcport->loop_id == FC_NO_LOOP_ID) { + fcport->loop_id = next_loopid = + ha->min_external_loopid; + status = qla2x00_find_new_loop_id( + vha, fcport); + if (status != QLA_SUCCESS) { + /* Ran out of IDs to use */ + break; + } + } + + if (IS_ALOGIO_CAPABLE(ha)) { + fcport->flags |= FCF_ASYNC_SENT; + data[0] = 0; + data[1] = QLA_LOGIO_LOGIN_RETRIED; + status = qla2x00_post_async_login_work( + vha, fcport, data); + if (status == QLA_SUCCESS) + continue; + /* Attempt a retry. */ + status = 1; + } else { + status = qla2x00_fabric_login(vha, + fcport, &next_loopid); + if (status == QLA_SUCCESS) { + int status2; + uint8_t opts; + + opts = 0; + if (fcport->flags & + FCF_FCP2_DEVICE) + opts |= BIT_1; + status2 = + qla2x00_get_port_database( + vha, fcport, opts); + if (status2 != QLA_SUCCESS) + status = 1; + } + } + } else + status = qla2x00_local_device_login(vha, + fcport); + + if (status == QLA_SUCCESS) { + fcport->old_loop_id = fcport->loop_id; + + ql_dbg(ql_dbg_disc, vha, 0x2003, + "Port login OK: logged in ID 0x%x.\n", + fcport->loop_id); + + qla2x00_update_fcport(vha, fcport); + + } else if (status == 1) { + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + /* retry the login again */ + ql_dbg(ql_dbg_disc, vha, 0x2007, + "Retrying %d login again loop_id 0x%x.\n", + fcport->login_retry, fcport->loop_id); + } else { + fcport->login_retry = 0; + } + + if (fcport->login_retry == 0 && status != QLA_SUCCESS) + fcport->loop_id = FC_NO_LOOP_ID; + } + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) + break; + } +out: + return; } /************************************************************************** @@ -2673,191 +4029,207 @@ static int qla2x00_do_dpc(void *data) { int rval; - scsi_qla_host_t *ha; - fc_port_t *fcport; - uint8_t status; - uint16_t next_loopid; - struct scsi_qla_host *vha; - int i; + scsi_qla_host_t *base_vha; + struct qla_hw_data *ha; - - ha = (scsi_qla_host_t *)data; + ha = (struct qla_hw_data *)data; + base_vha = pci_get_drvdata(ha->pdev); set_user_nice(current, -10); + set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { - DEBUG3(printk("qla2x00: DPC handler sleeping\n")); + ql_dbg(ql_dbg_dpc, base_vha, 0x4000, + "DPC handler sleeping.\n"); - set_current_state(TASK_INTERRUPTIBLE); schedule(); __set_current_state(TASK_RUNNING); - DEBUG3(printk("qla2x00: DPC handler waking up\n")); + if (!base_vha->flags.init_done || ha->flags.mbox_busy) + goto end_loop; - /* Initialization not yet finished. Don't do anything yet. */ - if (!ha->flags.init_done) - continue; - - DEBUG3(printk("scsi(%ld): DPC handler\n", ha->host_no)); + if (ha->flags.eeh_busy) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4003, + "eeh_busy=%d.\n", ha->flags.eeh_busy); + goto end_loop; + } ha->dpc_active = 1; - if (ha->flags.mbox_busy) { - ha->dpc_active = 0; - continue; + ql_dbg(ql_dbg_dpc + ql_dbg_verbose, base_vha, 0x4001, + "DPC handler waking up, dpc_flags=0x%lx.\n", + base_vha->dpc_flags); + + qla2x00_do_work(base_vha); + + if (IS_QLA82XX(ha)) { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + qla82xx_idc_lock(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_FAILED); + qla82xx_idc_unlock(ha); + ql_log(ql_log_info, base_vha, 0x4004, + "HW State: FAILED.\n"); + qla82xx_device_state_handler(base_vha); + continue; + } + + if (test_and_clear_bit(FCOE_CTX_RESET_NEEDED, + &base_vha->dpc_flags)) { + + ql_dbg(ql_dbg_dpc, base_vha, 0x4005, + "FCoE context reset scheduled.\n"); + if (!(test_and_set_bit(ABORT_ISP_ACTIVE, + &base_vha->dpc_flags))) { + if (qla82xx_fcoe_ctx_reset(base_vha)) { + /* FCoE-ctx reset failed. + * Escalate to chip-reset + */ + set_bit(ISP_ABORT_NEEDED, + &base_vha->dpc_flags); + } + clear_bit(ABORT_ISP_ACTIVE, + &base_vha->dpc_flags); + } + + ql_dbg(ql_dbg_dpc, base_vha, 0x4006, + "FCoE context reset end.\n"); + } } - qla2x00_do_work(ha); + if (test_and_clear_bit(ISP_ABORT_NEEDED, + &base_vha->dpc_flags)) { - if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) { - - DEBUG(printk("scsi(%ld): dpc: sched " - "qla2x00_abort_isp ha = %p\n", - ha->host_no, ha)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4007, + "ISP abort scheduled.\n"); if (!(test_and_set_bit(ABORT_ISP_ACTIVE, - &ha->dpc_flags))) { + &base_vha->dpc_flags))) { - if (qla2x00_abort_isp(ha)) { + if (ha->isp_ops->abort_isp(base_vha)) { /* failed. retry later */ set_bit(ISP_ABORT_NEEDED, - &ha->dpc_flags); + &base_vha->dpc_flags); } - clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); + clear_bit(ABORT_ISP_ACTIVE, + &base_vha->dpc_flags); } - for_each_mapped_vp_idx(ha, i) { - list_for_each_entry(vha, &ha->vp_list, - vp_list) { - if (i == vha->vp_idx) { - set_bit(ISP_ABORT_NEEDED, - &vha->dpc_flags); - break; - } - } - } - - DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n", - ha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4008, + "ISP abort end.\n"); } - if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) - qla2x00_update_fcports(ha); + if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) { + qla2x00_update_fcports(base_vha); + clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags); + } - if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) && - (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) { + if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4009, + "Quiescence mode scheduled.\n"); + qla82xx_device_state_handler(base_vha); + clear_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags); + if (!ha->flags.quiesce_owner) { + qla2x00_perform_loop_resync(base_vha); - DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n", - ha->host_no)); + qla82xx_idc_lock(ha); + qla82xx_clear_qsnt_ready(base_vha); + qla82xx_idc_unlock(ha); + } + ql_dbg(ql_dbg_dpc, base_vha, 0x400a, + "Quiescence mode end.\n"); + } - qla2x00_rst_aen(ha); - clear_bit(RESET_ACTIVE, &ha->dpc_flags); + if (test_and_clear_bit(RESET_MARKER_NEEDED, + &base_vha->dpc_flags) && + (!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) { + + ql_dbg(ql_dbg_dpc, base_vha, 0x400b, + "Reset marker scheduled.\n"); + qla2x00_rst_aen(base_vha); + clear_bit(RESET_ACTIVE, &base_vha->dpc_flags); + ql_dbg(ql_dbg_dpc, base_vha, 0x400c, + "Reset marker end.\n"); } /* Retry each device up to login retry count */ - if ((test_and_clear_bit(RELOGIN_NEEDED, &ha->dpc_flags)) && - !test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) && - atomic_read(&ha->loop_state) != LOOP_DOWN) { + if ((test_and_clear_bit(RELOGIN_NEEDED, + &base_vha->dpc_flags)) && + !test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) && + atomic_read(&base_vha->loop_state) != LOOP_DOWN) { - DEBUG(printk("scsi(%ld): qla2x00_port_login()\n", - ha->host_no)); - - next_loopid = 0; - list_for_each_entry_rcu(fcport, &ha->fcports, list) { - /* - * If the port is not ONLINE then try to login - * to it if we haven't run out of retries. - */ - if (atomic_read(&fcport->state) != FCS_ONLINE && - fcport->login_retry) { - - if (fcport->flags & FCF_FABRIC_DEVICE) { - if (fcport->flags & - FCF_TAPE_PRESENT) - ha->isp_ops->fabric_logout( - ha, fcport->loop_id, - fcport->d_id.b.domain, - fcport->d_id.b.area, - fcport->d_id.b.al_pa); - status = qla2x00_fabric_login( - ha, fcport, &next_loopid); - } else - status = - qla2x00_local_device_login( - ha, fcport); - - fcport->login_retry--; - if (status == QLA_SUCCESS) { - fcport->old_loop_id = fcport->loop_id; - - DEBUG(printk("scsi(%ld): port login OK: logged in ID 0x%x\n", - ha->host_no, fcport->loop_id)); - - qla2x00_update_fcport(ha, - fcport); - } else if (status == 1) { - set_bit(RELOGIN_NEEDED, &ha->dpc_flags); - /* retry the login again */ - DEBUG(printk("scsi(%ld): Retrying %d login again loop_id 0x%x\n", - ha->host_no, - fcport->login_retry, fcport->loop_id)); - } else { - fcport->login_retry = 0; - } - if (fcport->login_retry == 0 && status != QLA_SUCCESS) - fcport->loop_id = FC_NO_LOOP_ID; - } - if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) - break; - } - DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n", - ha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400d, + "Relogin scheduled.\n"); + qla2x00_relogin(base_vha); + ql_dbg(ql_dbg_dpc, base_vha, 0x400e, + "Relogin end.\n"); } - if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) { + if (test_and_clear_bit(LOOP_RESYNC_NEEDED, + &base_vha->dpc_flags)) { - DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n", - ha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400f, + "Loop resync scheduled.\n"); if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, - &ha->dpc_flags))) { + &base_vha->dpc_flags))) { - rval = qla2x00_loop_resync(ha); + rval = qla2x00_loop_resync(base_vha); - clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags); + clear_bit(LOOP_RESYNC_ACTIVE, + &base_vha->dpc_flags); } - DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n", - ha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4010, + "Loop resync end.\n"); + } + + if (test_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags) && + atomic_read(&base_vha->loop_state) == LOOP_READY) { + clear_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags); + qla2xxx_flash_npiv_conf(base_vha); } if (!ha->interrupts_on) ha->isp_ops->enable_intrs(ha); - if (test_and_clear_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags)) - ha->isp_ops->beacon_blink(ha); + if (test_and_clear_bit(BEACON_BLINK_NEEDED, + &base_vha->dpc_flags)) + ha->isp_ops->beacon_blink(base_vha); - qla2x00_do_dpc_all_vps(ha); + qla2x00_do_dpc_all_vps(base_vha); ha->dpc_active = 0; +end_loop: + set_current_state(TASK_INTERRUPTIBLE); } /* End of while(1) */ + __set_current_state(TASK_RUNNING); - DEBUG(printk("scsi(%ld): DPC handler exiting\n", ha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4011, + "DPC handler exiting.\n"); /* * Make sure that nobody tries to wake us up again. */ ha->dpc_active = 0; + /* Cleanup any residual CTX SRBs. */ + qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); + return 0; } void -qla2xxx_wake_dpc(scsi_qla_host_t *ha) +qla2xxx_wake_dpc(struct scsi_qla_host *vha) { + struct qla_hw_data *ha = vha->hw; + struct task_struct *t = ha->dpc_thread; unsigned long flags; + spin_lock_irqsave(&ha->dpc_lock, flags); - if (ha->dpc_thread) - wake_up_process(ha->dpc_thread); + if (!test_bit(UNLOADING, &vha->dpc_flags) && t) { + wake_up_process(t); + } spin_unlock_irqrestore(&ha->dpc_lock, flags); } @@ -2869,48 +4241,24 @@ qla2xxx_wake_dpc(scsi_qla_host_t *ha) * ha = adapter block pointer. */ static void -qla2x00_rst_aen(scsi_qla_host_t *ha) +qla2x00_rst_aen(scsi_qla_host_t *vha) { - if (ha->flags.online && !ha->flags.reset_active && - !atomic_read(&ha->loop_down_timer) && - !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) { + if (vha->flags.online && !vha->flags.reset_active && + !atomic_read(&vha->loop_down_timer) && + !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))) { do { - clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); /* * Issue marker command only when we are going to start * the I/O. */ - ha->marker_needed = 1; - } while (!atomic_read(&ha->loop_down_timer) && - (test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags))); + vha->marker_needed = 1; + } while (!atomic_read(&vha->loop_down_timer) && + (test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags))); } } -static void -qla2x00_sp_free_dma(scsi_qla_host_t *ha, srb_t *sp) -{ - struct scsi_cmnd *cmd = sp->cmd; - - if (sp->flags & SRB_DMA_VALID) { - scsi_dma_unmap(cmd); - sp->flags &= ~SRB_DMA_VALID; - } - CMD_SP(cmd) = NULL; -} - -void -qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp) -{ - struct scsi_cmnd *cmd = sp->cmd; - - qla2x00_sp_free_dma(ha, sp); - - mempool_free(sp, ha->srb_mempool); - - cmd->scsi_done(cmd); -} - /************************************************************************** * qla2x00_timer * @@ -2920,144 +4268,167 @@ qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp) * Context: Interrupt ***************************************************************************/ void -qla2x00_timer(scsi_qla_host_t *ha) +qla2x00_timer(scsi_qla_host_t *vha) { unsigned long cpu_flags = 0; - fc_port_t *fcport; int start_dpc = 0; int index; srb_t *sp; - int t; - scsi_qla_host_t *pha = to_qla_parent(ha); + uint16_t w; + struct qla_hw_data *ha = vha->hw; + struct req_que *req; - /* - * Ports - Port down timer. - * - * Whenever, a port is in the LOST state we start decrementing its port - * down timer every second until it reaches zero. Once it reaches zero - * the port it marked DEAD. - */ - t = 0; - list_for_each_entry_rcu(fcport, &ha->fcports, list) { -#ifndef CONFIG_SCSI_QLA2XXX_TARGET - if (fcport->port_type != FCT_TARGET) - continue; -#endif - if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) { - if (atomic_read(&fcport->port_down_timer) == 0) - continue; + if (ha->flags.eeh_busy) { + ql_dbg(ql_dbg_timer, vha, 0x6000, + "EEH = %d, restarting timer.\n", + ha->flags.eeh_busy); + qla2x00_restart_timer(vha, WATCH_INTERVAL); + return; + } - if (atomic_dec_and_test(&fcport->port_down_timer) != 0) - atomic_set(&fcport->state, FCS_DEVICE_DEAD); + /* Hardware read to raise pending EEH errors during mailbox waits. */ + if (!pci_channel_offline(ha->pdev)) + pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); - DEBUG(printk("scsi(%ld): fcport-%d - port retry count: " - "%d remaining\n", - ha->host_no, - t, atomic_read(&fcport->port_down_timer))); - } - t++; - } /* End of for fcport */ + /* Make sure qla82xx_watchdog is run only for physical port */ + if (!vha->vp_idx && IS_QLA82XX(ha)) { + if (test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) + start_dpc++; + qla82xx_watchdog(vha); + } /* Loop down handler. */ - if (atomic_read(&ha->loop_down_timer) > 0 && - !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) && ha->flags.online) { + if (atomic_read(&vha->loop_down_timer) > 0 && + !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) && + !(test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags)) + && vha->flags.online) { - if (atomic_read(&ha->loop_down_timer) == - ha->loop_down_abort_time) { + if (atomic_read(&vha->loop_down_timer) == + vha->loop_down_abort_time) { - DEBUG(printk("scsi(%ld): Loop Down - aborting the " - "queues before time expire\n", - ha->host_no)); + ql_log(ql_log_info, vha, 0x6008, + "Loop down - aborting the queues before time expires.\n"); - if (!IS_QLA2100(ha) && ha->link_down_timeout) - atomic_set(&ha->loop_state, LOOP_DEAD); + if (!IS_QLA2100(ha) && vha->link_down_timeout) + atomic_set(&vha->loop_state, LOOP_DEAD); - /* Schedule an ISP abort to return any tape commands. */ + /* + * Schedule an ISP abort to return any FCP2-device + * commands. + */ /* NPIV - scan physical port only */ - if (!ha->parent) { + if (!vha->vp_idx) { spin_lock_irqsave(&ha->hardware_lock, cpu_flags); + req = ha->req_q_map[0]; for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { fc_port_t *sfcp; - sp = ha->outstanding_cmds[index]; + sp = req->outstanding_cmds[index]; if (!sp) continue; + if (sp->type != SRB_SCSI_CMD) + continue; sfcp = sp->fcport; - if (!(sfcp->flags & FCF_TAPE_PRESENT)) + if (!(sfcp->flags & FCF_FCP2_DEVICE)) continue; - set_bit(ISP_ABORT_NEEDED, - &ha->dpc_flags); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, + &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); break; } spin_unlock_irqrestore(&ha->hardware_lock, - cpu_flags); + cpu_flags); } - set_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags); start_dpc++; } /* if the loop has been down for 4 minutes, reinit adapter */ - if (atomic_dec_and_test(&ha->loop_down_timer) != 0) { - DEBUG(printk("scsi(%ld): Loop down exceed 4 mins - " - "restarting queues.\n", - ha->host_no)); - - set_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags); - start_dpc++; - - if (!(ha->device_flags & DFLG_NO_CABLE) && - !ha->parent) { - DEBUG(printk("scsi(%ld): Loop down - " - "aborting ISP.\n", - ha->host_no)); - qla_printk(KERN_WARNING, ha, + if (atomic_dec_and_test(&vha->loop_down_timer) != 0) { + if (!(vha->device_flags & DFLG_NO_CABLE)) { + ql_log(ql_log_warn, vha, 0x6009, "Loop down - aborting ISP.\n"); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, + &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); } } - DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n", - ha->host_no, - atomic_read(&ha->loop_down_timer))); + ql_dbg(ql_dbg_timer, vha, 0x600a, + "Loop down - seconds remaining %d.\n", + atomic_read(&vha->loop_down_timer)); } - /* Check if beacon LED needs to be blinked */ - if (ha->beacon_blink_led == 1) { - set_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags); - start_dpc++; + /* Check if beacon LED needs to be blinked for physical host only */ + if (!vha->vp_idx && (ha->beacon_blink_led == 1)) { + /* There is no beacon_blink function for ISP82xx */ + if (!IS_QLA82XX(ha)) { + set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags); + start_dpc++; + } } /* Process any deferred work. */ - if (!list_empty(&ha->work_list)) + if (!list_empty(&vha->work_list)) start_dpc++; /* Schedule the DPC routine if needed */ - if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || - test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || - test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags) || + if ((test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) || + test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags) || start_dpc || - test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) || - test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) || - test_bit(VP_DPC_NEEDED, &ha->dpc_flags) || - test_bit(RELOGIN_NEEDED, &ha->dpc_flags))) - qla2xxx_wake_dpc(pha); + test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) || + test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags) || + test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) || + test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) || + test_bit(VP_DPC_NEEDED, &vha->dpc_flags) || + test_bit(RELOGIN_NEEDED, &vha->dpc_flags))) { + ql_dbg(ql_dbg_timer, vha, 0x600b, + "isp_abort_needed=%d loop_resync_needed=%d " + "fcport_update_needed=%d start_dpc=%d " + "reset_marker_needed=%d", + test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags), + test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags), + test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags), + start_dpc, + test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags)); + ql_dbg(ql_dbg_timer, vha, 0x600c, + "beacon_blink_needed=%d isp_unrecoverable=%d " + "fcoe_ctx_reset_needed=%d vp_dpc_needed=%d " + "relogin_needed=%d.\n", + test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags), + test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags), + test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags), + test_bit(VP_DPC_NEEDED, &vha->dpc_flags), + test_bit(RELOGIN_NEEDED, &vha->dpc_flags)); + qla2xxx_wake_dpc(vha); + } - qla2x00_restart_timer(ha, WATCH_INTERVAL); + qla2x00_restart_timer(vha, WATCH_INTERVAL); } /* Firmware interface routines. */ -#define FW_BLOBS 6 +#define FW_BLOBS 10 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 #define FW_ISP2322 3 #define FW_ISP24XX 4 #define FW_ISP25XX 5 +#define FW_ISP81XX 6 +#define FW_ISP82XX 7 +#define FW_ISP2031 8 +#define FW_ISP8031 9 #define FW_FILE_ISP21XX "ql2100_fw.bin" #define FW_FILE_ISP22XX "ql2200_fw.bin" @@ -3065,6 +4436,10 @@ qla2x00_timer(scsi_qla_host_t *ha) #define FW_FILE_ISP2322 "ql2322_fw.bin" #define FW_FILE_ISP24XX "ql2400_fw.bin" #define FW_FILE_ISP25XX "ql2500_fw.bin" +#define FW_FILE_ISP81XX "ql8100_fw.bin" +#define FW_FILE_ISP82XX "ql8200_fw.bin" +#define FW_FILE_ISP2031 "ql2600_fw.bin" +#define FW_FILE_ISP8031 "ql8300_fw.bin" static DEFINE_MUTEX(qla_fw_lock); @@ -3075,14 +4450,18 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = FW_FILE_ISP2322, .segs = { 0x800, 0x1c000, 0x1e000, 0 }, }, { .name = FW_FILE_ISP24XX, }, { .name = FW_FILE_ISP25XX, }, + { .name = FW_FILE_ISP81XX, }, + { .name = FW_FILE_ISP82XX, }, + { .name = FW_FILE_ISP2031, }, + { .name = FW_FILE_ISP8031, }, }; struct fw_blob * -qla2x00_request_firmware(scsi_qla_host_t *ha) +qla2x00_request_firmware(scsi_qla_host_t *vha) { + struct qla_hw_data *ha = vha->hw; struct fw_blob *blob; - blob = NULL; if (IS_QLA2100(ha)) { blob = &qla_fw_blobs[FW_ISP21XX]; } else if (IS_QLA2200(ha)) { @@ -3095,6 +4474,16 @@ qla2x00_request_firmware(scsi_qla_host_t *ha) blob = &qla_fw_blobs[FW_ISP24XX]; } else if (IS_QLA25XX(ha)) { blob = &qla_fw_blobs[FW_ISP25XX]; + } else if (IS_QLA81XX(ha)) { + blob = &qla_fw_blobs[FW_ISP81XX]; + } else if (IS_QLA82XX(ha)) { + blob = &qla_fw_blobs[FW_ISP82XX]; + } else if (IS_QLA2031(ha)) { + blob = &qla_fw_blobs[FW_ISP2031]; + } else if (IS_QLA8031(ha)) { + blob = &qla_fw_blobs[FW_ISP8031]; + } else { + return NULL; } mutex_lock(&qla_fw_lock); @@ -3102,8 +4491,8 @@ qla2x00_request_firmware(scsi_qla_host_t *ha) goto out; if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) { - DEBUG2(printk("scsi(%ld): Failed to load firmware image " - "(%s).\n", ha->host_no, blob->name)); + ql_log(ql_log_warn, vha, 0x0063, + "Failed to load firmware image (%s).\n", blob->name); blob->fw = NULL; blob = NULL; goto out; @@ -3129,14 +4518,32 @@ qla2x00_release_firmware(void) static pci_ers_result_t qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { + scsi_qla_host_t *vha = pci_get_drvdata(pdev); + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_aer, vha, 0x9000, + "PCI error detected, state %x.\n", state); + switch (state) { case pci_channel_io_normal: + ha->flags.eeh_busy = 0; return PCI_ERS_RESULT_CAN_RECOVER; case pci_channel_io_frozen: + ha->flags.eeh_busy = 1; + /* For ISP82XX complete any pending mailbox cmd */ + if (IS_QLA82XX(ha)) { + ha->flags.isp82xx_fw_hung = 1; + ql_dbg(ql_dbg_aer, vha, 0x9001, "Pci channel io frozen\n"); + qla82xx_clear_pending_mbx(vha); + } + qla2x00_free_irqs(vha); pci_disable_device(pdev); + /* Return back all IOs */ + qla2x00_abort_all_cmds(vha, DID_RESET << 16); return PCI_ERS_RESULT_NEED_RESET; case pci_channel_io_perm_failure: - qla2x00_remove_one(pdev); + ha->flags.pci_channel_io_perm_failure = 1; + qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16); return PCI_ERS_RESULT_DISCONNECT; } return PCI_ERS_RESULT_NEED_RESET; @@ -3148,10 +4555,14 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) int risc_paused = 0; uint32_t stat; unsigned long flags; - scsi_qla_host_t *ha = pci_get_drvdata(pdev); + scsi_qla_host_t *base_vha = pci_get_drvdata(pdev); + struct qla_hw_data *ha = base_vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; + if (IS_QLA82XX(ha)) + return PCI_ERS_RESULT_RECOVERED; + spin_lock_irqsave(&ha->hardware_lock, flags); if (IS_QLA2100(ha) || IS_QLA2200(ha)){ stat = RD_REG_DWORD(®->hccr); @@ -3169,21 +4580,150 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) spin_unlock_irqrestore(&ha->hardware_lock, flags); if (risc_paused) { - qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, " - "Dumping firmware!\n"); - ha->isp_ops->fw_dump(ha, 0); + ql_log(ql_log_info, base_vha, 0x9003, + "RISC paused -- mmio_enabled, Dumping firmware.\n"); + ha->isp_ops->fw_dump(base_vha, 0); return PCI_ERS_RESULT_NEED_RESET; } else return PCI_ERS_RESULT_RECOVERED; } +uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) +{ + uint32_t rval = QLA_FUNCTION_FAILED; + uint32_t drv_active = 0; + struct qla_hw_data *ha = base_vha->hw; + int fn; + struct pci_dev *other_pdev = NULL; + + ql_dbg(ql_dbg_aer, base_vha, 0x9006, + "Entered %s.\n", __func__); + + set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + + if (base_vha->flags.online) { + /* Abort all outstanding commands, + * so as to be requeued later */ + qla2x00_abort_isp_cleanup(base_vha); + } + + + fn = PCI_FUNC(ha->pdev->devfn); + while (fn > 0) { + fn--; + ql_dbg(ql_dbg_aer, base_vha, 0x9007, + "Finding pci device at function = 0x%x.\n", fn); + other_pdev = +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) + pci_get_bus_and_slot( +#else + pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus), +#endif + ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn), + fn)); + + if (!other_pdev) + continue; + if (atomic_read(&other_pdev->enable_cnt)) { + ql_dbg(ql_dbg_aer, base_vha, 0x9008, + "Found PCI func available and enable at 0x%x.\n", + fn); + pci_dev_put(other_pdev); + break; + } + pci_dev_put(other_pdev); + } + + if (!fn) { + /* Reset owner */ + ql_dbg(ql_dbg_aer, base_vha, 0x9009, + "This devfn is reset owner = 0x%x.\n", + ha->pdev->devfn); + qla82xx_idc_lock(ha); + + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_INITIALIZING); + + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, + QLA82XX_IDC_VERSION); + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + ql_dbg(ql_dbg_aer, base_vha, 0x900a, + "drv_active = 0x%x.\n", drv_active); + + qla82xx_idc_unlock(ha); + /* Reset if device is not already reset + * drv_active would be 0 if a reset has already been done + */ + if (drv_active) + rval = qla82xx_start_firmware(base_vha); + else + rval = QLA_SUCCESS; + qla82xx_idc_lock(ha); + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_info, base_vha, 0x900b, + "HW State: FAILED.\n"); + qla82xx_clear_drv_active(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_FAILED); + } else { + ql_log(ql_log_info, base_vha, 0x900c, + "HW State: READY.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_READY); + qla82xx_idc_unlock(ha); + ha->flags.isp82xx_fw_hung = 0; + rval = qla82xx_restart_isp(base_vha); + qla82xx_idc_lock(ha); + /* Clear driver state register */ + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0); + qla82xx_set_drv_active(base_vha); + } + qla82xx_idc_unlock(ha); + } else { + ql_dbg(ql_dbg_aer, base_vha, 0x900d, + "This devfn is not reset owner = 0x%x.\n", + ha->pdev->devfn); + if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) == + QLA82XX_DEV_READY)) { + ha->flags.isp82xx_fw_hung = 0; + rval = qla82xx_restart_isp(base_vha); + qla82xx_idc_lock(ha); + qla82xx_set_drv_active(base_vha); + qla82xx_idc_unlock(ha); + } + } + clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + + return rval; +} + static pci_ers_result_t qla2xxx_pci_slot_reset(struct pci_dev *pdev) { pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT; - scsi_qla_host_t *ha = pci_get_drvdata(pdev); - int rc; + scsi_qla_host_t *base_vha = pci_get_drvdata(pdev); + struct qla_hw_data *ha = base_vha->hw; + struct rsp_que *rsp; + int rc, retries = 10; + + ql_dbg(ql_dbg_aer, base_vha, 0x9004, + "Slot Reset.\n"); + + /* Workaround: qla2xxx driver which access hardware earlier + * needs error state to be pci_channel_io_online. + * Otherwise mailbox command timesout. + */ + pdev->error_state = pci_channel_io_normal; + + pci_restore_state(pdev); + + /* pci_restore_state() clears the saved_state flag of the device + * save restored state which resets saved_state flag + */ + pci_save_state(pdev); if (ha->mem_only) rc = pci_enable_device_mem(pdev); @@ -3191,20 +4731,37 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) rc = pci_enable_device(pdev); if (rc) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, base_vha, 0x9005, "Can't re-enable PCI device after reset.\n"); - - return ret; + goto exit_slot_reset; } - pci_set_master(pdev); - if (ha->isp_ops->pci_config(ha)) - return ret; + rsp = ha->rsp_q_map[0]; + if (qla2x00_request_irqs(ha, rsp)) + goto exit_slot_reset; - set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); - if (qla2x00_abort_isp(ha)== QLA_SUCCESS) + if (ha->isp_ops->pci_config(base_vha)) + goto exit_slot_reset; + + if (IS_QLA82XX(ha)) { + if (qla82xx_error_recovery(base_vha) == QLA_SUCCESS) { + ret = PCI_ERS_RESULT_RECOVERED; + goto exit_slot_reset; + } else + goto exit_slot_reset; + } + + while (ha->flags.mbox_busy && retries--) + msleep(1000); + + set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + if (ha->isp_ops->abort_isp(base_vha) == QLA_SUCCESS) ret = PCI_ERS_RESULT_RECOVERED; - clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); + clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + +exit_slot_reset: + ql_dbg(ql_dbg_aer, base_vha, 0x900e, + "slot_reset return %x.\n", ret); return ret; } @@ -3212,16 +4769,22 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) static void qla2xxx_pci_resume(struct pci_dev *pdev) { - scsi_qla_host_t *ha = pci_get_drvdata(pdev); + scsi_qla_host_t *base_vha = pci_get_drvdata(pdev); + struct qla_hw_data *ha = base_vha->hw; int ret; - ret = qla2x00_wait_for_hba_online(ha); + ql_dbg(ql_dbg_aer, base_vha, 0x900f, + "pci_resume.\n"); + + ret = qla2x00_wait_for_hba_online(base_vha); if (ret != QLA_SUCCESS) { - qla_printk(KERN_ERR, ha, - "the device failed to resume I/O " - "from slot/link_reset"); + ql_log(ql_log_fatal, base_vha, 0x9002, + "The device failed to resume I/O from slot/link_reset.\n"); } + pci_cleanup_aer_uncorrect_error_status(pdev); + + ha->flags.eeh_busy = 0; } static struct pci_error_handlers qla2xxx_err_handler = { @@ -3245,6 +4808,10 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2031) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8021) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8031) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); @@ -3257,54 +4824,58 @@ static struct pci_driver qla2xxx_pci_driver = { .id_table = qla2xxx_pci_tbl, .probe = qla2x00_probe_one, .remove = qla2x00_remove_one, + .shutdown = qla2x00_shutdown, .err_handler = &qla2xxx_err_handler, }; +static struct file_operations apidev_fops = { + .owner = THIS_MODULE, +}; #ifdef CONFIG_SCSI_QLA2XXX_TARGET /* Must be called under HW lock */ -void qla_set_tgt_mode(scsi_qla_host_t *ha) +void qla_set_tgt_mode(scsi_qla_host_t *vha) { switch (ql2x_ini_mode) { case QLA2X_INI_MODE_DISABLED: case QLA2X_INI_MODE_EXCLUSIVE: - ha->host->active_mode = MODE_TARGET; + vha->host->active_mode = MODE_TARGET; break; case QLA2X_INI_MODE_ENABLED: - ha->host->active_mode |= MODE_TARGET; + vha->host->active_mode |= MODE_TARGET; break; default: BUG(); break; } - if (ha->ini_mode_force_reverse) - qla_reverse_ini_mode(ha); + if (vha->ini_mode_force_reverse) + qla_reverse_ini_mode(vha); return; } EXPORT_SYMBOL(qla_set_tgt_mode); /* Must be called under HW lock */ -void qla_clear_tgt_mode(scsi_qla_host_t *ha) +void qla_clear_tgt_mode(scsi_qla_host_t *vha) { switch (ql2x_ini_mode) { case QLA2X_INI_MODE_DISABLED: - ha->host->active_mode = MODE_UNKNOWN; + vha->host->active_mode = MODE_UNKNOWN; break; case QLA2X_INI_MODE_EXCLUSIVE: - ha->host->active_mode = MODE_INITIATOR; + vha->host->active_mode = MODE_INITIATOR; break; case QLA2X_INI_MODE_ENABLED: - ha->host->active_mode &= ~MODE_TARGET; + vha->host->active_mode &= ~MODE_TARGET; break; default: BUG(); break; } - if (ha->ini_mode_force_reverse) - qla_reverse_ini_mode(ha); + if (vha->ini_mode_force_reverse) + qla_reverse_ini_mode(vha); return; } @@ -3334,38 +4905,19 @@ qla2x00_module_init(void) { int ret = 0; -#if defined(QL_DEBUG_LEVEL_1) || \ - defined(QL_DEBUG_LEVEL_2) || \ - defined(QL_DEBUG_LEVEL_3) || \ - defined(QL_DEBUG_LEVEL_4) || \ - defined(QL_DEBUG_LEVEL_5) || \ - defined(QL_DEBUG_LEVEL_6) || \ - defined(QL_DEBUG_LEVEL_7) || \ - defined(QL_DEBUG_LEVEL_8) || \ - defined(QL_DEBUG_LEVEL_9) || \ - defined(QL_DEBUG_LEVEL_10) || \ - defined(QL_DEBUG_LEVEL_11) || \ - defined(QL_DEBUG_LEVEL_12) || \ - defined(QL_DEBUG_LEVEL_13) || \ - defined(QL_DEBUG_LEVEL_14) || \ - defined(QL_DEBUG_LEVEL_15) || \ - defined(QL_DEBUG_LEVEL_16) - ql2xextended_error_logging = 1; -#endif - #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (!qla2x00_parse_ini_mode()) { printk("Wrong qlini_mode value %s\n", qlini_mode); return -EINVAL; } -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ /* Allocate cache for SRBs. */ srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0, SLAB_HWCACHE_ALIGN, NULL); if (srb_cachep == NULL) { - printk(KERN_ERR - "qla2xxx: Unable to allocate SRB cache...Failing load!\n"); + ql_log(ql_log_fatal, NULL, 0x0001, + "Unable to allocate SRB cache...Failing load!.\n"); return -ENOMEM; } @@ -3378,23 +4930,37 @@ qla2x00_module_init(void) fc_attach_transport(&qla2xxx_transport_functions); if (!qla2xxx_transport_template) { kmem_cache_destroy(srb_cachep); + ql_log(ql_log_fatal, NULL, 0x0002, + "fc_attach_transport failed...Failing load!.\n"); return -ENODEV; } + + apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops); + if (apidev_major < 0) { + ql_log(ql_log_fatal, NULL, 0x0003, + "Unable to register char device %s.\n", QLA2XXX_APIDEV); + } + qla2xxx_transport_vport_template = fc_attach_transport(&qla2xxx_transport_vport_functions); if (!qla2xxx_transport_vport_template) { kmem_cache_destroy(srb_cachep); fc_release_transport(qla2xxx_transport_template); + ql_log(ql_log_fatal, NULL, 0x0004, + "fc_attach_transport vport failed...Failing load!.\n"); return -ENODEV; } - - printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n", + ql_log(ql_log_info, NULL, 0x0005, + "QLogic Fibre Channel HBA Driver: %s.\n", qla2x00_version_str); ret = pci_register_driver(&qla2xxx_pci_driver); if (ret) { kmem_cache_destroy(srb_cachep); fc_release_transport(qla2xxx_transport_template); fc_release_transport(qla2xxx_transport_vport_template); + ql_log(ql_log_fatal, NULL, 0x0006, + "pci_register_driver failed...ret=%d Failing load!.\n", + ret); } return ret; } @@ -3405,9 +4971,12 @@ qla2x00_module_init(void) static void __exit qla2x00_module_exit(void) { + unregister_chrdev(apidev_major, QLA2XXX_APIDEV); pci_unregister_driver(&qla2xxx_pci_driver); qla2x00_release_firmware(); kmem_cache_destroy(srb_cachep); + if (ctx_cachep) + kmem_cache_destroy(ctx_cachep); fc_release_transport(qla2xxx_transport_template); fc_release_transport(qla2xxx_transport_vport_template); } @@ -3415,12 +4984,14 @@ qla2x00_module_exit(void) module_init(qla2x00_module_init); module_exit(qla2x00_module_exit); -MODULE_AUTHOR("QLogic Corporation & SCST team"); #ifdef CONFIG_SCSI_QLA2XXX_TARGET -MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver (Target Mode Support, including 24xx+ ISP)"); +MODULE_AUTHOR("QLogic Corporation & SCST team"); +MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver (Target Mode Support, up " + "to 26xx/83xx ISP)"); #else +MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver"); -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ MODULE_LICENSE("GPL"); MODULE_VERSION(QLA2XXX_VERSION); MODULE_FIRMWARE(FW_FILE_ISP21XX); diff --git a/qla2x00t/qla_settings.h b/qla2x00t/qla_settings.h index 2801c2664..d70f03008 100644 --- a/qla2x00t/qla_settings.h +++ b/qla2x00t/qla_settings.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ diff --git a/qla2x00t/qla_sup.c b/qla2x00t/qla_sup.c index 4caa560b8..50401764f 100644 --- a/qla2x00t/qla_sup.c +++ b/qla2x00t/qla_sup.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -10,10 +10,6 @@ #include #include -static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t); -static void qla2x00_nv_deselect(scsi_qla_host_t *); -static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t); - /* * NVRAM support routines */ @@ -23,7 +19,7 @@ static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t); * @ha: HA context */ static void -qla2x00_lock_nvram_access(scsi_qla_host_t *ha) +qla2x00_lock_nvram_access(struct qla_hw_data *ha) { uint16_t data; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -56,7 +52,7 @@ qla2x00_lock_nvram_access(scsi_qla_host_t *ha) * @ha: HA context */ static void -qla2x00_unlock_nvram_access(scsi_qla_host_t *ha) +qla2x00_unlock_nvram_access(struct qla_hw_data *ha) { struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -66,6 +62,84 @@ qla2x00_unlock_nvram_access(scsi_qla_host_t *ha) } } +/** + * qla2x00_nv_write() - Prepare for NVRAM read/write operation. + * @ha: HA context + * @data: Serial interface selector + */ +static void +qla2x00_nv_write(struct qla_hw_data *ha, uint16_t data) +{ + struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + + WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_CLOCK | + NVR_WRT_ENABLE); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); +} + +/** + * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from + * NVRAM. + * @ha: HA context + * @nv_cmd: NVRAM command + * + * Bit definitions for NVRAM command: + * + * Bit 26 = start bit + * Bit 25, 24 = opcode + * Bit 23-16 = address + * Bit 15-0 = write data + * + * Returns the word read from nvram @addr. + */ +static uint16_t +qla2x00_nvram_request(struct qla_hw_data *ha, uint32_t nv_cmd) +{ + uint8_t cnt; + struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + uint16_t data = 0; + uint16_t reg_data; + + /* Send command to NVRAM. */ + nv_cmd <<= 5; + for (cnt = 0; cnt < 11; cnt++) { + if (nv_cmd & BIT_31) + qla2x00_nv_write(ha, NVR_DATA_OUT); + else + qla2x00_nv_write(ha, 0); + nv_cmd <<= 1; + } + + /* Read data from NVRAM. */ + for (cnt = 0; cnt < 16; cnt++) { + WRT_REG_WORD(®->nvram, NVR_SELECT | NVR_CLOCK); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + data <<= 1; + reg_data = RD_REG_WORD(®->nvram); + if (reg_data & NVR_DATA_IN) + data |= BIT_0; + WRT_REG_WORD(®->nvram, NVR_SELECT); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + } + + /* Deselect chip. */ + WRT_REG_WORD(®->nvram, NVR_DESELECT); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + + return data; +} + + /** * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the * request routine to get the word from NVRAM. @@ -75,7 +149,7 @@ qla2x00_unlock_nvram_access(scsi_qla_host_t *ha) * Returns the word read from nvram @addr. */ static uint16_t -qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr) +qla2x00_get_nvram_word(struct qla_hw_data *ha, uint32_t addr) { uint16_t data; uint32_t nv_cmd; @@ -87,6 +161,20 @@ qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr) return (data); } +/** + * qla2x00_nv_deselect() - Deselect NVRAM operations. + * @ha: HA context + */ +static void +qla2x00_nv_deselect(struct qla_hw_data *ha) +{ + struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + + WRT_REG_WORD(®->nvram, NVR_DESELECT); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); +} + /** * qla2x00_write_nvram_word() - Write NVRAM data. * @ha: HA context @@ -94,12 +182,13 @@ qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr) * @data: word to program */ static void -qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data) +qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data) { int count; uint16_t word; uint32_t nv_cmd, wait_cnt; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); qla2x00_nv_write(ha, NVR_DATA_OUT); qla2x00_nv_write(ha, 0); @@ -131,8 +220,8 @@ qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data) wait_cnt = NVR_WAIT_CNT; do { if (!--wait_cnt) { - DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n", - __func__, ha->host_no)); + ql_dbg(ql_dbg_user, vha, 0x708d, + "NVRAM didn't go ready...\n"); break; } NVRAM_DELAY(); @@ -150,8 +239,8 @@ qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data) } static int -qla2x00_write_nvram_word_tmo(scsi_qla_host_t *ha, uint32_t addr, uint16_t data, - uint32_t tmo) +qla2x00_write_nvram_word_tmo(struct qla_hw_data *ha, uint32_t addr, + uint16_t data, uint32_t tmo) { int ret, count; uint16_t word; @@ -208,108 +297,18 @@ qla2x00_write_nvram_word_tmo(scsi_qla_host_t *ha, uint32_t addr, uint16_t data, return ret; } -/** - * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from - * NVRAM. - * @ha: HA context - * @nv_cmd: NVRAM command - * - * Bit definitions for NVRAM command: - * - * Bit 26 = start bit - * Bit 25, 24 = opcode - * Bit 23-16 = address - * Bit 15-0 = write data - * - * Returns the word read from nvram @addr. - */ -static uint16_t -qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd) -{ - uint8_t cnt; - struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - uint16_t data = 0; - uint16_t reg_data; - - /* Send command to NVRAM. */ - nv_cmd <<= 5; - for (cnt = 0; cnt < 11; cnt++) { - if (nv_cmd & BIT_31) - qla2x00_nv_write(ha, NVR_DATA_OUT); - else - qla2x00_nv_write(ha, 0); - nv_cmd <<= 1; - } - - /* Read data from NVRAM. */ - for (cnt = 0; cnt < 16; cnt++) { - WRT_REG_WORD(®->nvram, NVR_SELECT | NVR_CLOCK); - RD_REG_WORD(®->nvram); /* PCI Posting. */ - NVRAM_DELAY(); - data <<= 1; - reg_data = RD_REG_WORD(®->nvram); - if (reg_data & NVR_DATA_IN) - data |= BIT_0; - WRT_REG_WORD(®->nvram, NVR_SELECT); - RD_REG_WORD(®->nvram); /* PCI Posting. */ - NVRAM_DELAY(); - } - - /* Deselect chip. */ - WRT_REG_WORD(®->nvram, NVR_DESELECT); - RD_REG_WORD(®->nvram); /* PCI Posting. */ - NVRAM_DELAY(); - - return (data); -} - -/** - * qla2x00_nv_write() - Clean NVRAM operations. - * @ha: HA context - */ -static void -qla2x00_nv_deselect(scsi_qla_host_t *ha) -{ - struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - - WRT_REG_WORD(®->nvram, NVR_DESELECT); - RD_REG_WORD(®->nvram); /* PCI Posting. */ - NVRAM_DELAY(); -} - -/** - * qla2x00_nv_write() - Prepare for NVRAM read/write operation. - * @ha: HA context - * @data: Serial interface selector - */ -static void -qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data) -{ - struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - - WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); - RD_REG_WORD(®->nvram); /* PCI Posting. */ - NVRAM_DELAY(); - WRT_REG_WORD(®->nvram, data | NVR_SELECT| NVR_CLOCK | - NVR_WRT_ENABLE); - RD_REG_WORD(®->nvram); /* PCI Posting. */ - NVRAM_DELAY(); - WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); - RD_REG_WORD(®->nvram); /* PCI Posting. */ - NVRAM_DELAY(); -} - /** * qla2x00_clear_nvram_protection() - * @ha: HA context */ static int -qla2x00_clear_nvram_protection(scsi_qla_host_t *ha) +qla2x00_clear_nvram_protection(struct qla_hw_data *ha) { int ret, stat; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint32_t word, wait_cnt; uint16_t wprot, wprot_old; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); /* Clear NVRAM write protection. */ ret = QLA_FUNCTION_FAILED; @@ -352,9 +351,8 @@ qla2x00_clear_nvram_protection(scsi_qla_host_t *ha) wait_cnt = NVR_WAIT_CNT; do { if (!--wait_cnt) { - DEBUG9_10(printk("%s(%ld): NVRAM didn't go " - "ready...\n", __func__, - ha->host_no)); + ql_dbg(ql_dbg_user, vha, 0x708e, + "NVRAM didn't go ready...\n"); break; } NVRAM_DELAY(); @@ -370,10 +368,11 @@ qla2x00_clear_nvram_protection(scsi_qla_host_t *ha) } static void -qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat) +qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat) { struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint32_t word, wait_cnt; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); if (stat != QLA_SUCCESS) return; @@ -412,8 +411,8 @@ qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat) wait_cnt = NVR_WAIT_CNT; do { if (!--wait_cnt) { - DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n", - __func__, ha->host_no)); + ql_dbg(ql_dbg_user, vha, 0x708f, + "NVRAM didn't go ready...\n"); break; } NVRAM_DELAY(); @@ -426,35 +425,32 @@ qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat) /* Flash Manipulation Routines */ /*****************************************************************************/ -#define OPTROM_BURST_SIZE 0x1000 -#define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4) - static inline uint32_t -flash_conf_to_access_addr(uint32_t faddr) +flash_conf_addr(struct qla_hw_data *ha, uint32_t faddr) { - return FARX_ACCESS_FLASH_CONF | faddr; + return ha->flash_conf_off | faddr; } static inline uint32_t -flash_data_to_access_addr(uint32_t faddr) +flash_data_addr(struct qla_hw_data *ha, uint32_t faddr) { - return FARX_ACCESS_FLASH_DATA | faddr; + return ha->flash_data_off | faddr; } static inline uint32_t -nvram_conf_to_access_addr(uint32_t naddr) +nvram_conf_addr(struct qla_hw_data *ha, uint32_t naddr) { - return FARX_ACCESS_NVRAM_CONF | naddr; + return ha->nvram_conf_off | naddr; } static inline uint32_t -nvram_data_to_access_addr(uint32_t naddr) +nvram_data_addr(struct qla_hw_data *ha, uint32_t naddr) { - return FARX_ACCESS_NVRAM_DATA | naddr; + return ha->nvram_data_off | naddr; } static uint32_t -qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr) +qla24xx_read_flash_dword(struct qla_hw_data *ha, uint32_t addr) { int rval; uint32_t cnt, data; @@ -482,21 +478,22 @@ qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr) } uint32_t * -qla24xx_read_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, +qla24xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { uint32_t i; + struct qla_hw_data *ha = vha->hw; /* Dword reads to flash. */ for (i = 0; i < dwords; i++, faddr++) dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, - flash_data_to_access_addr(faddr))); + flash_data_addr(ha, faddr))); return dwptr; } static int -qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data) +qla24xx_write_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t data) { int rval; uint32_t cnt; @@ -519,12 +516,12 @@ qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data) } static void -qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, +qla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id, uint8_t *flash_id) { uint32_t ids; - ids = qla24xx_read_flash_dword(ha, flash_data_to_access_addr(0xd03ab)); + ids = qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x03ab)); *man_id = LSB(ids); *flash_id = MSB(ids); @@ -536,30 +533,344 @@ qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, * Example: ATMEL 0x00 01 45 1F * Extract MFG and Dev ID from last two bytes. */ - ids = qla24xx_read_flash_dword(ha, - flash_data_to_access_addr(0xd009f)); + ids = qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x009f)); *man_id = LSB(ids); *flash_id = MSB(ids); } } -void -qla2xxx_get_flash_info(scsi_qla_host_t *ha) +static int +qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start) { + const char *loc, *locations[] = { "DEF", "PCI" }; + uint32_t pcihdr, pcids; + uint32_t *dcode; + uint8_t *buf, *bcode, last_image; + uint16_t cnt, chksum, *wptr; + struct qla_flt_location *fltl; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + /* + * FLT-location structure resides after the last PCI region. + */ + + /* Begin with sane defaults. */ + loc = locations[0]; + *start = 0; + if (IS_QLA24XX_TYPE(ha)) + *start = FA_FLASH_LAYOUT_ADDR_24; + else if (IS_QLA25XX(ha)) + *start = FA_FLASH_LAYOUT_ADDR; + else if (IS_QLA81XX(ha)) + *start = FA_FLASH_LAYOUT_ADDR_81; + else if (IS_QLA82XX(ha)) { + *start = FA_FLASH_LAYOUT_ADDR_82; + goto end; + } else if (IS_QLA83XX(ha)) { + *start = FA_FLASH_LAYOUT_ADDR_83; + goto end; + } + /* Begin with first PCI expansion ROM header. */ + buf = (uint8_t *)req->ring; + dcode = (uint32_t *)req->ring; + pcihdr = 0; + last_image = 1; + do { + /* Verify PCI expansion ROM header. */ + qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20); + bcode = buf + (pcihdr % 4); + if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) + goto end; + + /* Locate PCI data structure. */ + pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]); + qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20); + bcode = buf + (pcihdr % 4); + + /* Validate signature of PCI data structure. */ + if (bcode[0x0] != 'P' || bcode[0x1] != 'C' || + bcode[0x2] != 'I' || bcode[0x3] != 'R') + goto end; + + last_image = bcode[0x15] & BIT_7; + + /* Locate next PCI expansion ROM. */ + pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512; + } while (!last_image); + + /* Now verify FLT-location structure. */ + fltl = (struct qla_flt_location *)req->ring; + qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, + sizeof(struct qla_flt_location) >> 2); + if (fltl->sig[0] != 'Q' || fltl->sig[1] != 'F' || + fltl->sig[2] != 'L' || fltl->sig[3] != 'T') + goto end; + + wptr = (uint16_t *)req->ring; + cnt = sizeof(struct qla_flt_location) >> 1; + for (chksum = 0; cnt; cnt--) + chksum += le16_to_cpu(*wptr++); + if (chksum) { + ql_log(ql_log_fatal, vha, 0x0045, + "Inconsistent FLTL detected: checksum=0x%x.\n", chksum); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010e, + buf, sizeof(struct qla_flt_location)); + return QLA_FUNCTION_FAILED; + } + + /* Good data. Use specified location. */ + loc = locations[1]; + *start = (le16_to_cpu(fltl->start_hi) << 16 | + le16_to_cpu(fltl->start_lo)) >> 2; +end: + ql_dbg(ql_dbg_init, vha, 0x0046, + "FLTL[%s] = 0x%x.\n", + loc, *start); + return QLA_SUCCESS; +} + +static void +qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) +{ + const char *loc, *locations[] = { "DEF", "FLT" }; + const uint32_t def_fw[] = + { FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR_81 }; + const uint32_t def_boot[] = + { FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR_81 }; + const uint32_t def_vpd_nvram[] = + { FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR_81 }; + const uint32_t def_vpd0[] = + { 0, 0, FA_VPD0_ADDR_81 }; + const uint32_t def_vpd1[] = + { 0, 0, FA_VPD1_ADDR_81 }; + const uint32_t def_nvram0[] = + { 0, 0, FA_NVRAM0_ADDR_81 }; + const uint32_t def_nvram1[] = + { 0, 0, FA_NVRAM1_ADDR_81 }; + const uint32_t def_fdt[] = + { FA_FLASH_DESCR_ADDR_24, FA_FLASH_DESCR_ADDR, + FA_FLASH_DESCR_ADDR_81 }; + const uint32_t def_npiv_conf0[] = + { FA_NPIV_CONF0_ADDR_24, FA_NPIV_CONF0_ADDR, + FA_NPIV_CONF0_ADDR_81 }; + const uint32_t def_npiv_conf1[] = + { FA_NPIV_CONF1_ADDR_24, FA_NPIV_CONF1_ADDR, + FA_NPIV_CONF1_ADDR_81 }; + const uint32_t fcp_prio_cfg0[] = + { FA_FCP_PRIO0_ADDR, FA_FCP_PRIO0_ADDR_25, + 0 }; + const uint32_t fcp_prio_cfg1[] = + { FA_FCP_PRIO1_ADDR, FA_FCP_PRIO1_ADDR_25, + 0 }; + uint32_t def; + uint16_t *wptr; + uint16_t cnt, chksum; + uint32_t start; + struct qla_flt_header *flt; + struct qla_flt_region *region; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + def = 0; + if (IS_QLA25XX(ha)) + def = 1; + else if (IS_QLA81XX(ha)) + def = 2; + + /* Assign FCP prio region since older adapters may not have FLT, or + FCP prio region in it's FLT. + */ + ha->flt_region_fcp_prio = ha->flags.port0 ? + fcp_prio_cfg0[def] : fcp_prio_cfg1[def]; + + ha->flt_region_flt = flt_addr; + wptr = (uint16_t *)req->ring; + flt = (struct qla_flt_header *)req->ring; + region = (struct qla_flt_region *)&flt[1]; + ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring, + flt_addr << 2, OPTROM_BURST_SIZE); + if (*wptr == __constant_cpu_to_le16(0xffff)) + goto no_flash_data; + if (flt->version != __constant_cpu_to_le16(1)) { + ql_log(ql_log_warn, vha, 0x0047, + "Unsupported FLT detected: version=0x%x length=0x%x checksum=0x%x.\n", + le16_to_cpu(flt->version), le16_to_cpu(flt->length), + le16_to_cpu(flt->checksum)); + goto no_flash_data; + } + + cnt = (sizeof(struct qla_flt_header) + le16_to_cpu(flt->length)) >> 1; + for (chksum = 0; cnt; cnt--) + chksum += le16_to_cpu(*wptr++); + if (chksum) { + ql_log(ql_log_fatal, vha, 0x0048, + "Inconsistent FLT detected: version=0x%x length=0x%x checksum=0x%x.\n", + le16_to_cpu(flt->version), le16_to_cpu(flt->length), + le16_to_cpu(flt->checksum)); + goto no_flash_data; + } + + loc = locations[1]; + cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region); + for ( ; cnt; cnt--, region++) { + /* Store addresses as DWORD offsets. */ + start = le32_to_cpu(region->start) >> 2; + ql_dbg(ql_dbg_init, vha, 0x0049, + "FLT[%02x]: start=0x%x " + "end=0x%x size=0x%x.\n", le32_to_cpu(region->code), + start, le32_to_cpu(region->end) >> 2, + le32_to_cpu(region->size)); + + switch (le32_to_cpu(region->code) & 0xff) { + case FLT_REG_FCOE_FW: + if (!IS_QLA8031(ha)) + break; + ha->flt_region_fw = start; + break; + case FLT_REG_FW: + if (IS_QLA8031(ha)) + break; + ha->flt_region_fw = start; + break; + case FLT_REG_BOOT_CODE: + ha->flt_region_boot = start; + break; + case FLT_REG_VPD_0: + if (IS_QLA8031(ha)) + break; + ha->flt_region_vpd_nvram = start; + if (IS_QLA82XX(ha)) + break; + if (ha->flags.port0) + ha->flt_region_vpd = start; + break; + case FLT_REG_VPD_1: + if (IS_QLA82XX(ha) || IS_QLA8031(ha)) + break; + if (!ha->flags.port0) + ha->flt_region_vpd = start; + break; + case FLT_REG_NVRAM_0: + if (IS_QLA8031(ha)) + break; + if (ha->flags.port0) + ha->flt_region_nvram = start; + break; + case FLT_REG_NVRAM_1: + if (IS_QLA8031(ha)) + break; + if (!ha->flags.port0) + ha->flt_region_nvram = start; + break; + case FLT_REG_FDT: + ha->flt_region_fdt = start; + break; + case FLT_REG_NPIV_CONF_0: + if (ha->flags.port0) + ha->flt_region_npiv_conf = start; + break; + case FLT_REG_NPIV_CONF_1: + if (!ha->flags.port0) + ha->flt_region_npiv_conf = start; + break; + case FLT_REG_GOLD_FW: + ha->flt_region_gold_fw = start; + break; + case FLT_REG_FCP_PRIO_0: + if (ha->flags.port0) + ha->flt_region_fcp_prio = start; + break; + case FLT_REG_FCP_PRIO_1: + if (!ha->flags.port0) + ha->flt_region_fcp_prio = start; + break; + case FLT_REG_BOOT_CODE_82XX: + ha->flt_region_boot = start; + break; + case FLT_REG_FW_82XX: + ha->flt_region_fw = start; + break; + case FLT_REG_GOLD_FW_82XX: + ha->flt_region_gold_fw = start; + break; + case FLT_REG_BOOTLOAD_82XX: + ha->flt_region_bootload = start; + break; + case FLT_REG_VPD_82XX: + ha->flt_region_vpd = start; + break; + case FLT_REG_FCOE_VPD_0: + if (!IS_QLA8031(ha)) + break; + ha->flt_region_vpd_nvram = start; + if (ha->flags.port0) + ha->flt_region_vpd = start; + break; + case FLT_REG_FCOE_VPD_1: + if (!IS_QLA8031(ha)) + break; + if (!ha->flags.port0) + ha->flt_region_vpd = start; + break; + case FLT_REG_FCOE_NVRAM_0: + if (!IS_QLA8031(ha)) + break; + if (ha->flags.port0) + ha->flt_region_nvram = start; + break; + case FLT_REG_FCOE_NVRAM_1: + if (!IS_QLA8031(ha)) + break; + if (!ha->flags.port0) + ha->flt_region_nvram = start; + break; + } + } + goto done; + +no_flash_data: + /* Use hardcoded defaults. */ + loc = locations[0]; + ha->flt_region_fw = def_fw[def]; + ha->flt_region_boot = def_boot[def]; + ha->flt_region_vpd_nvram = def_vpd_nvram[def]; + ha->flt_region_vpd = ha->flags.port0 ? + def_vpd0[def] : def_vpd1[def]; + ha->flt_region_nvram = ha->flags.port0 ? + def_nvram0[def] : def_nvram1[def]; + ha->flt_region_fdt = def_fdt[def]; + ha->flt_region_npiv_conf = ha->flags.port0 ? + def_npiv_conf0[def] : def_npiv_conf1[def]; +done: + ql_dbg(ql_dbg_init, vha, 0x004a, + "FLT[%s]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x nvram=0x%x " + "fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x.\n", + loc, ha->flt_region_boot, ha->flt_region_fw, + ha->flt_region_vpd_nvram, ha->flt_region_vpd, ha->flt_region_nvram, + ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_npiv_conf, + ha->flt_region_fcp_prio); +} + +static void +qla2xxx_get_fdt_info(scsi_qla_host_t *vha) +{ +#define FLASH_BLK_SIZE_4K 0x1000 #define FLASH_BLK_SIZE_32K 0x8000 #define FLASH_BLK_SIZE_64K 0x10000 + const char *loc, *locations[] = { "MID", "FDT" }; uint16_t cnt, chksum; uint16_t *wptr; struct qla_fdt_layout *fdt; uint8_t man_id, flash_id; + uint16_t mid = 0, fid = 0; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) - return; - - wptr = (uint16_t *)ha->request_ring; - fdt = (struct qla_fdt_layout *)ha->request_ring; - ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring, - FA_FLASH_DESCR_ADDR << 2, OPTROM_BURST_SIZE); + wptr = (uint16_t *)req->ring; + fdt = (struct qla_fdt_layout *)req->ring; + ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring, + ha->flt_region_fdt << 2, OPTROM_BURST_SIZE); if (*wptr == __constant_cpu_to_le16(0xffff)) goto no_flash_data; if (fdt->sig[0] != 'Q' || fdt->sig[1] != 'L' || fdt->sig[2] != 'I' || @@ -570,36 +881,40 @@ qla2xxx_get_flash_info(scsi_qla_host_t *ha) cnt++) chksum += le16_to_cpu(*wptr++); if (chksum) { - DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent FDT detected: " - "checksum=0x%x id=%c version=0x%x.\n", chksum, fdt->sig[0], - le16_to_cpu(fdt->version))); - DEBUG9(qla2x00_dump_buffer((uint8_t *)fdt, sizeof(*fdt))); + ql_dbg(ql_dbg_init, vha, 0x004c, + "Inconsistent FDT detected:" + " checksum=0x%x id=%c version0x%x.\n", chksum, + fdt->sig[0], le16_to_cpu(fdt->version)); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113, + (uint8_t *)fdt, sizeof(*fdt)); goto no_flash_data; } - ha->fdt_odd_index = le16_to_cpu(fdt->man_id) == 0x1f; + loc = locations[1]; + mid = le16_to_cpu(fdt->man_id); + fid = le16_to_cpu(fdt->id); ha->fdt_wrt_disable = fdt->wrt_disable_bits; - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0300 | fdt->erase_cmd); + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0300 | fdt->erase_cmd); ha->fdt_block_size = le32_to_cpu(fdt->block_size); if (fdt->unprotect_sec_cmd) { - ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0300 | + ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0300 | fdt->unprotect_sec_cmd); ha->fdt_protect_sec_cmd = fdt->protect_sec_cmd ? - flash_conf_to_access_addr(0x0300 | fdt->protect_sec_cmd): - flash_conf_to_access_addr(0x0336); + flash_conf_addr(ha, 0x0300 | fdt->protect_sec_cmd): + flash_conf_addr(ha, 0x0336); } - - DEBUG2(qla_printk(KERN_DEBUG, ha, "Flash[FDT]: (0x%x/0x%x) erase=0x%x " - "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", - le16_to_cpu(fdt->man_id), le16_to_cpu(fdt->id), ha->fdt_erase_cmd, - ha->fdt_protect_sec_cmd, ha->fdt_unprotect_sec_cmd, - ha->fdt_odd_index, ha->fdt_wrt_disable, ha->fdt_block_size)); - return; - + goto done; no_flash_data: + loc = locations[0]; + if (IS_QLA82XX(ha)) { + ha->fdt_block_size = FLASH_BLK_SIZE_64K; + goto done; + } qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id); + mid = man_id; + fid = flash_id; ha->fdt_wrt_disable = 0x9c; - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x03d8); + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x03d8); switch (man_id) { case 0xbf: /* STT flash. */ if (flash_id == 0x8e) @@ -608,65 +923,225 @@ no_flash_data: ha->fdt_block_size = FLASH_BLK_SIZE_32K; if (flash_id == 0x80) - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0352); + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0352); break; case 0x13: /* ST M25P80. */ ha->fdt_block_size = FLASH_BLK_SIZE_64K; break; case 0x1f: /* Atmel 26DF081A. */ - ha->fdt_odd_index = 1; - ha->fdt_block_size = FLASH_BLK_SIZE_64K; - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0320); - ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0339); - ha->fdt_protect_sec_cmd = flash_conf_to_access_addr(0x0336); + ha->fdt_block_size = FLASH_BLK_SIZE_4K; + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0320); + ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0339); + ha->fdt_protect_sec_cmd = flash_conf_addr(ha, 0x0336); break; default: /* Default to 64 kb sector size. */ ha->fdt_block_size = FLASH_BLK_SIZE_64K; break; } - - DEBUG2(qla_printk(KERN_DEBUG, ha, "Flash[MID]: (0x%x/0x%x) erase=0x%x " - "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", man_id, flash_id, +done: + ql_dbg(ql_dbg_init, vha, 0x004d, + "FDT[%s]: (0x%x/0x%x) erase=0x%x " + "pr=%x wrtd=0x%x blk=0x%x.\n", + loc, mid, fid, ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd, - ha->fdt_unprotect_sec_cmd, ha->fdt_odd_index, ha->fdt_wrt_disable, - ha->fdt_block_size)); + ha->fdt_wrt_disable, ha->fdt_block_size); + } static void -qla24xx_unprotect_flash(scsi_qla_host_t *ha) +qla2xxx_get_idc_param(scsi_qla_host_t *vha) { +#define QLA82XX_IDC_PARAM_ADDR 0x003e885c + uint32_t *wptr; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + if (!IS_QLA82XX(ha)) + return; + + wptr = (uint32_t *)req->ring; + ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring, + QLA82XX_IDC_PARAM_ADDR , 8); + + if (*wptr == __constant_cpu_to_le32(0xffffffff)) { + ha->nx_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; + ha->nx_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT; + } else { + ha->nx_dev_init_timeout = le32_to_cpu(*wptr++); + ha->nx_reset_timeout = le32_to_cpu(*wptr); + } + ql_dbg(ql_dbg_init, vha, 0x004e, + "nx_dev_init_timeout=%d " + "nx_reset_timeout=%d.\n", ha->nx_dev_init_timeout, + ha->nx_reset_timeout); + return; +} + +int +qla2xxx_get_flash_info(scsi_qla_host_t *vha) +{ + int ret; + uint32_t flt_addr; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) + return QLA_SUCCESS; + + ret = qla2xxx_find_flt_start(vha, &flt_addr); + if (ret != QLA_SUCCESS) + return ret; + + qla2xxx_get_flt_info(vha, flt_addr); + qla2xxx_get_fdt_info(vha); + qla2xxx_get_idc_param(vha); + + return QLA_SUCCESS; +} + +void +qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) +{ +#define NPIV_CONFIG_SIZE (16*1024) + void *data; + uint16_t *wptr; + uint16_t cnt, chksum; + int i; + struct qla_npiv_header hdr; + struct qla_npiv_entry *entry; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) + return; + + if (ha->flags.isp82xx_reset_hdlr_active) + return; + + ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, + ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header)); + if (hdr.version == __constant_cpu_to_le16(0xffff)) + return; + if (hdr.version != __constant_cpu_to_le16(1)) { + ql_dbg(ql_dbg_user, vha, 0x7090, + "Unsupported NPIV-Config " + "detected: version=0x%x entries=0x%x checksum=0x%x.\n", + le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries), + le16_to_cpu(hdr.checksum)); + return; + } + + data = kmalloc(NPIV_CONFIG_SIZE, GFP_KERNEL); + if (!data) { + ql_log(ql_log_warn, vha, 0x7091, + "Unable to allocate memory for data.\n"); + return; + } + + ha->isp_ops->read_optrom(vha, (uint8_t *)data, + ha->flt_region_npiv_conf << 2, NPIV_CONFIG_SIZE); + + cnt = (sizeof(struct qla_npiv_header) + le16_to_cpu(hdr.entries) * + sizeof(struct qla_npiv_entry)) >> 1; + for (wptr = data, chksum = 0; cnt; cnt--) + chksum += le16_to_cpu(*wptr++); + if (chksum) { + ql_dbg(ql_dbg_user, vha, 0x7092, + "Inconsistent NPIV-Config " + "detected: version=0x%x entries=0x%x checksum=0x%x.\n", + le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries), + le16_to_cpu(hdr.checksum)); + goto done; + } + + entry = data + sizeof(struct qla_npiv_header); + cnt = le16_to_cpu(hdr.entries); + for (i = 0; cnt; cnt--, entry++, i++) { + uint16_t flags; + struct fc_vport_identifiers vid; + struct fc_vport *vport; + + memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry)); + + flags = le16_to_cpu(entry->flags); + if (flags == 0xffff) + continue; + if ((flags & BIT_0) == 0) + continue; + + memset(&vid, 0, sizeof(vid)); + vid.roles = FC_PORT_ROLE_FCP_INITIATOR; + vid.vport_type = FC_PORTTYPE_NPIV; + vid.disable = false; + vid.port_name = wwn_to_u64(entry->port_name); + vid.node_name = wwn_to_u64(entry->node_name); + + ql_dbg(ql_dbg_user, vha, 0x7093, + "NPIV[%02x]: wwpn=%llx " + "wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt, + (unsigned long long)vid.port_name, + (unsigned long long)vid.node_name, + le16_to_cpu(entry->vf_id), + entry->q_qos, entry->f_qos); + + if (i < QLA_PRECONFIG_VPORTS) { + vport = fc_vport_create(vha->host, 0, &vid); + if (!vport) + ql_log(ql_log_warn, vha, 0x7094, + "NPIV-Config Failed to create vport [%02x]: " + "wwpn=%llx wwnn=%llx.\n", cnt, + (unsigned long long)vid.port_name, + (unsigned long long)vid.node_name); + } + } +done: + kfree(data); +} + +static int +qla24xx_unprotect_flash(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + if (ha->flags.fac_supported) + return qla81xx_fac_do_write_enable(vha, 1); + /* Enable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ if (!ha->fdt_wrt_disable) - return; + goto done; - /* Disable flash write-protection. */ - qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); - /* Some flash parts need an additional zero-write to clear bits.*/ - qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); + /* Disable flash write-protection, first clear SR protection bit */ + qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0); + /* Then write zero again to clear remaining SR bits.*/ + qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0); +done: + return QLA_SUCCESS; } -static void -qla24xx_protect_flash(scsi_qla_host_t *ha) +static int +qla24xx_protect_flash(scsi_qla_host_t *vha) { uint32_t cnt; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + if (ha->flags.fac_supported) + return qla81xx_fac_do_write_enable(vha, 0); + if (!ha->fdt_wrt_disable) goto skip_wrt_protect; /* Enable flash write-protection and wait for completion. */ - qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), + qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), ha->fdt_wrt_disable); for (cnt = 300; cnt && - qla24xx_read_flash_dword(ha, - flash_conf_to_access_addr(0x005)) & BIT_0; + qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x005)) & BIT_0; cnt--) { udelay(10); } @@ -676,63 +1151,79 @@ skip_wrt_protect: WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ + + return QLA_SUCCESS; } static int -qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, +qla24xx_erase_sector(scsi_qla_host_t *vha, uint32_t fdata) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t start, finish; + + if (ha->flags.fac_supported) { + start = fdata >> 2; + finish = start + (ha->fdt_block_size >> 2) - 1; + return qla81xx_fac_erase_sector(vha, flash_data_addr(ha, + start), flash_data_addr(ha, finish)); + } + + return qla24xx_write_flash_dword(ha, ha->fdt_erase_cmd, + (fdata & 0xff00) | ((fdata << 16) & 0xff0000) | + ((fdata >> 16) & 0xff)); +} + +static int +qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { int ret; - uint32_t liter, miter; + uint32_t liter; uint32_t sec_mask, rest_addr; - uint32_t fdata, findex; + uint32_t fdata; dma_addr_t optrom_dma; void *optrom = NULL; - uint32_t *s, *d; - - ret = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; /* Prepare burst-capable write on supported ISPs. */ - if (IS_QLA25XX(ha) && !(faddr & 0xfff) && - dwords > OPTROM_BURST_DWORDS) { + if ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha)) && + !(faddr & 0xfff) && dwords > OPTROM_BURST_DWORDS) { optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, &optrom_dma, GFP_KERNEL); if (!optrom) { - qla_printk(KERN_DEBUG, ha, - "Unable to allocate memory for optrom burst write " - "(%x KB).\n", OPTROM_BURST_SIZE / 1024); + ql_log(ql_log_warn, vha, 0x7095, + "Unable to allocate " + "memory for optrom burst write (%x KB).\n", + OPTROM_BURST_SIZE / 1024); } } rest_addr = (ha->fdt_block_size >> 2) - 1; - sec_mask = 0x80000 - (ha->fdt_block_size >> 2); + sec_mask = ~rest_addr; - qla24xx_unprotect_flash(ha); + ret = qla24xx_unprotect_flash(vha); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7096, + "Unable to unprotect flash for update.\n"); + goto done; + } for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { - if (ha->fdt_odd_index) { - findex = faddr << 2; - fdata = findex & sec_mask; - } else { - findex = faddr; - fdata = (findex & sec_mask) << 2; - } + fdata = (faddr & sec_mask) << 2; /* Are we at the beginning of a sector? */ - if ((findex & rest_addr) == 0) { + if ((faddr & rest_addr) == 0) { /* Do sector unprotect. */ if (ha->fdt_unprotect_sec_cmd) qla24xx_write_flash_dword(ha, ha->fdt_unprotect_sec_cmd, (fdata & 0xff00) | ((fdata << 16) & 0xff0000) | ((fdata >> 16) & 0xff)); - ret = qla24xx_write_flash_dword(ha, ha->fdt_erase_cmd, - (fdata & 0xff00) |((fdata << 16) & - 0xff0000) | ((fdata >> 16) & 0xff)); + ret = qla24xx_erase_sector(vha, fdata); if (ret != QLA_SUCCESS) { - DEBUG9(printk("%s(%ld) Unable to flash " - "sector: address=%x.\n", __func__, - ha->host_no, faddr)); + ql_dbg(ql_dbg_user, vha, 0x7007, + "Unable to erase erase sector: address=%x.\n", + faddr); break; } } @@ -740,20 +1231,18 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, /* Go with burst-write. */ if (optrom && (liter + OPTROM_BURST_DWORDS) <= dwords) { /* Copy data to DMA'ble buffer. */ - for (miter = 0, s = optrom, d = dwptr; - miter < OPTROM_BURST_DWORDS; miter++, s++, d++) - *s = cpu_to_le32(*d); + memcpy(optrom, dwptr, OPTROM_BURST_SIZE); - ret = qla2x00_load_ram(ha, optrom_dma, - flash_data_to_access_addr(faddr), + ret = qla2x00_load_ram(vha, optrom_dma, + flash_data_addr(ha, faddr), OPTROM_BURST_DWORDS); if (ret != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7097, "Unable to burst-write optrom segment " "(%x/%x/%llx).\n", ret, - flash_data_to_access_addr(faddr), + flash_data_addr(ha, faddr), (unsigned long long)optrom_dma); - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7098, "Reverting to slow-write.\n"); dma_free_coherent(&ha->pdev->dev, @@ -768,11 +1257,11 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, } ret = qla24xx_write_flash_dword(ha, - flash_data_to_access_addr(faddr), cpu_to_le32(*dwptr)); + flash_data_addr(ha, faddr), cpu_to_le32(*dwptr)); if (ret != QLA_SUCCESS) { - DEBUG9(printk("%s(%ld) Unable to program flash " - "address=%x data=%x.\n", __func__, - ha->host_no, faddr, *dwptr)); + ql_dbg(ql_dbg_user, vha, 0x7006, + "Unable to program flash address=%x data=%x.\n", + faddr, *dwptr); break; } @@ -785,8 +1274,11 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, 0xff0000) | ((fdata >> 16) & 0xff)); } - qla24xx_protect_flash(ha); - + ret = qla24xx_protect_flash(vha); + if (ret != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0x7099, + "Unable to protect flash after update.\n"); +done: if (optrom) dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom, optrom_dma); @@ -795,11 +1287,12 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, } uint8_t * -qla2x00_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, +qla2x00_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t bytes) { uint32_t i; uint16_t *wptr; + struct qla_hw_data *ha = vha->hw; /* Word reads to NVRAM via registers. */ wptr = (uint16_t *)buf; @@ -813,34 +1306,38 @@ qla2x00_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, } uint8_t * -qla24xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, +qla24xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t bytes) { uint32_t i; uint32_t *dwptr; + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA82XX(ha)) + return buf; /* Dword reads to flash. */ dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++) dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, - nvram_data_to_access_addr(naddr))); + nvram_data_addr(ha, naddr))); return buf; } int -qla2x00_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, +qla2x00_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t bytes) { int ret, stat; uint32_t i; uint16_t *wptr; unsigned long flags; - scsi_qla_host_t *pha = to_qla_parent(ha); + struct qla_hw_data *ha = vha->hw; ret = QLA_SUCCESS; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); qla2x00_lock_nvram_access(ha); /* Disable NVRAM write-protection. */ @@ -857,93 +1354,91 @@ qla2x00_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, qla2x00_set_nvram_protection(ha, stat); qla2x00_unlock_nvram_access(ha); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return ret; } int -qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, +qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t bytes) { int ret; uint32_t i; uint32_t *dwptr; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - unsigned long flags; - scsi_qla_host_t *pha = to_qla_parent(ha); ret = QLA_SUCCESS; - spin_lock_irqsave(&pha->hardware_lock, flags); + if (IS_QLA82XX(ha)) + return ret; + /* Enable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ /* Disable NVRAM write-protection. */ - qla24xx_write_flash_dword(ha, nvram_conf_to_access_addr(0x101), - 0); - qla24xx_write_flash_dword(ha, nvram_conf_to_access_addr(0x101), - 0); + qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0); + qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0); /* Dword writes to flash. */ dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++, dwptr++) { ret = qla24xx_write_flash_dword(ha, - nvram_data_to_access_addr(naddr), - cpu_to_le32(*dwptr)); + nvram_data_addr(ha, naddr), cpu_to_le32(*dwptr)); if (ret != QLA_SUCCESS) { - DEBUG9(printk("%s(%ld) Unable to program " - "nvram address=%x data=%x.\n", __func__, - ha->host_no, naddr, *dwptr)); + ql_dbg(ql_dbg_user, vha, 0x709a, + "Unable to program nvram address=%x data=%x.\n", + naddr, *dwptr); break; } } /* Enable NVRAM write-protection. */ - qla24xx_write_flash_dword(ha, nvram_conf_to_access_addr(0x101), - 0x8c); + qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0x8c); /* Disable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ - spin_unlock_irqrestore(&pha->hardware_lock, flags); return ret; } uint8_t * -qla25xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, +qla25xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t bytes) { uint32_t i; uint32_t *dwptr; + struct qla_hw_data *ha = vha->hw; /* Dword reads to flash. */ dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++) dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, - flash_data_to_access_addr(FA_VPD_NVRAM_ADDR | naddr))); + flash_data_addr(ha, ha->flt_region_vpd_nvram | naddr))); return buf; } int -qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, +qla25xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t bytes) { + struct qla_hw_data *ha = vha->hw; #define RMW_BUFFER_SIZE (64 * 1024) uint8_t *dbuf; dbuf = vmalloc(RMW_BUFFER_SIZE); if (!dbuf) return QLA_MEMORY_ALLOC_FAILED; - ha->isp_ops->read_optrom(ha, dbuf, FA_VPD_NVRAM_ADDR << 2, + ha->isp_ops->read_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2, RMW_BUFFER_SIZE); memcpy(dbuf + (naddr << 2), buf, bytes); - ha->isp_ops->write_optrom(ha, dbuf, FA_VPD_NVRAM_ADDR << 2, + ha->isp_ops->write_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2, RMW_BUFFER_SIZE); vfree(dbuf); @@ -951,7 +1446,7 @@ qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, } static inline void -qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags) +qla2x00_flip_colors(struct qla_hw_data *ha, uint16_t *pflags) { if (IS_QLA2322(ha)) { /* Flip all colors. */ @@ -981,16 +1476,19 @@ qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags) #define PIO_REG(h, r) ((h)->pio_address + offsetof(struct device_reg_2xxx, r)) void -qla2x00_beacon_blink(struct scsi_qla_host *ha) +qla2x00_beacon_blink(struct scsi_qla_host *vha) { uint16_t gpio_enable; uint16_t gpio_data; uint16_t led_color = 0; unsigned long flags; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - scsi_qla_host_t *pha = to_qla_parent(ha); - spin_lock_irqsave(&pha->hardware_lock, flags); + if (IS_QLA82XX(ha)) + return; + + spin_lock_irqsave(&ha->hardware_lock, flags); /* Save the Original GPIOE. */ if (ha->pio_address) { @@ -1027,29 +1525,29 @@ qla2x00_beacon_blink(struct scsi_qla_host *ha) RD_REG_WORD(®->gpiod); } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); } int -qla2x00_beacon_on(struct scsi_qla_host *ha) +qla2x00_beacon_on(struct scsi_qla_host *vha) { uint16_t gpio_enable; uint16_t gpio_data; unsigned long flags; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - scsi_qla_host_t *pha = to_qla_parent(ha); ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING; ha->fw_options[1] |= FO1_DISABLE_GPIO6_7; - if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x709b, "Unable to update fw options (beacon on).\n"); return QLA_FUNCTION_FAILED; } /* Turn off LEDs. */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); if (ha->pio_address) { gpio_enable = RD_REG_WORD_PIO(PIO_REG(ha, gpioe)); gpio_data = RD_REG_WORD_PIO(PIO_REG(ha, gpiod)); @@ -1075,7 +1573,7 @@ qla2x00_beacon_on(struct scsi_qla_host *ha) WRT_REG_WORD(®->gpiod, gpio_data); RD_REG_WORD(®->gpiod); } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); /* * Let the per HBA timer kick off the blinking process based on @@ -1088,9 +1586,10 @@ qla2x00_beacon_on(struct scsi_qla_host *ha) } int -qla2x00_beacon_off(struct scsi_qla_host *ha) +qla2x00_beacon_off(struct scsi_qla_host *vha) { int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; ha->beacon_blink_led = 0; @@ -1100,21 +1599,21 @@ qla2x00_beacon_off(struct scsi_qla_host *ha) else ha->beacon_color_state = QLA_LED_GRN_ON; - ha->isp_ops->beacon_blink(ha); /* This turns green LED off */ + ha->isp_ops->beacon_blink(vha); /* This turns green LED off */ ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING; ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7; - rval = qla2x00_set_fw_options(ha, ha->fw_options); + rval = qla2x00_set_fw_options(vha, ha->fw_options); if (rval != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x709c, "Unable to update fw options (beacon off).\n"); return rval; } static inline void -qla24xx_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags) +qla24xx_flip_colors(struct qla_hw_data *ha, uint16_t *pflags) { /* Flip all colors. */ if (ha->beacon_color_state == QLA_LED_ALL_ON) { @@ -1129,16 +1628,16 @@ qla24xx_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags) } void -qla24xx_beacon_blink(struct scsi_qla_host *ha) +qla24xx_beacon_blink(struct scsi_qla_host *vha) { uint16_t led_color = 0; uint32_t gpio_data; unsigned long flags; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - scsi_qla_host_t *pha = to_qla_parent(ha); /* Save the Original GPIOD. */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); gpio_data = RD_REG_DWORD(®->gpiod); /* Enable the gpio_data reg for update. */ @@ -1159,33 +1658,106 @@ qla24xx_beacon_blink(struct scsi_qla_host *ha) /* Set the modified gpio_data values. */ WRT_REG_DWORD(®->gpiod, gpio_data); gpio_data = RD_REG_DWORD(®->gpiod); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void +qla83xx_beacon_blink(struct scsi_qla_host *vha) +{ + uint32_t led_select_value; + struct qla_hw_data *ha = vha->hw; + uint16_t led_cfg[6]; + uint16_t orig_led_cfg[6]; + + if (!IS_QLA83XX(ha) && !IS_QLA81XX(ha)) + return; + + if (IS_QLA2031(ha) && ha->beacon_blink_led) { + if (ha->flags.port0) + led_select_value = 0x00201320; + else + led_select_value = 0x00201328; + + qla83xx_write_remote_reg(vha, led_select_value, 0x40002000); + qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40002000); + msleep(1000); + qla83xx_write_remote_reg(vha, led_select_value, 0x40004000); + qla83xx_write_remote_reg(vha, led_select_value + 4 , 0x40004000); + } else if ((IS_QLA8031(ha) || IS_QLA81XX(ha)) && ha->beacon_blink_led) { + int rval; + + /* Save Current */ + rval = qla81xx_get_led_config(vha, orig_led_cfg); + /* Do the blink */ + if (rval == QLA_SUCCESS) { + if (IS_QLA81XX(ha)) { + led_cfg[0] = 0x4000; + led_cfg[1] = 0x2000; + led_cfg[2] = 0; + led_cfg[3] = 0; + led_cfg[4] = 0; + led_cfg[5] = 0; + } else { + led_cfg[0] = 0x4000; + led_cfg[1] = 0x4000; + led_cfg[2] = 0x4000; + led_cfg[3] = 0x2000; + led_cfg[4] = 0; + led_cfg[5] = 0x2000; + } + rval = qla81xx_set_led_config(vha, led_cfg); + msleep(1000); + if (IS_QLA81XX(ha)) { + led_cfg[0] = 0x4000; + led_cfg[1] = 0x2000; + led_cfg[2] = 0; + } else { + led_cfg[0] = 0x4000; + led_cfg[1] = 0x2000; + led_cfg[2] = 0x4000; + led_cfg[3] = 0x4000; + led_cfg[4] = 0; + led_cfg[5] = 0x2000; + } + rval = qla81xx_set_led_config(vha, led_cfg); + } + /* On exit, restore original (presumes no status change) */ + qla81xx_set_led_config(vha, orig_led_cfg); + } } int -qla24xx_beacon_on(struct scsi_qla_host *ha) +qla24xx_beacon_on(struct scsi_qla_host *vha) { uint32_t gpio_data; unsigned long flags; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - if (ha->beacon_blink_led == 0) { - scsi_qla_host_t *pha = to_qla_parent(ha); + if (IS_QLA82XX(ha)) + return QLA_SUCCESS; + if (IS_QLA8031(ha) || IS_QLA81XX(ha)) + goto skip_gpio; /* let blink handle it */ + + if (ha->beacon_blink_led == 0) { /* Enable firmware for update */ ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL; - if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) + if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) return QLA_FUNCTION_FAILED; - if (qla2x00_get_fw_options(ha, ha->fw_options) != + if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7009, "Unable to update fw options (beacon on).\n"); return QLA_FUNCTION_FAILED; } - spin_lock_irqsave(&pha->hardware_lock, flags); + if (IS_QLA2031(ha)) + goto skip_gpio; + + spin_lock_irqsave(&ha->hardware_lock, flags); gpio_data = RD_REG_DWORD(®->gpiod); /* Enable the gpio_data reg for update. */ @@ -1193,12 +1765,13 @@ qla24xx_beacon_on(struct scsi_qla_host *ha) WRT_REG_DWORD(®->gpiod, gpio_data); RD_REG_DWORD(®->gpiod); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); } /* So all colors blink together. */ ha->beacon_color_state = 0; +skip_gpio: /* Let the per HBA timer kick off the blinking process. */ ha->beacon_blink_led = 1; @@ -1206,39 +1779,50 @@ qla24xx_beacon_on(struct scsi_qla_host *ha) } int -qla24xx_beacon_off(struct scsi_qla_host *ha) +qla24xx_beacon_off(struct scsi_qla_host *vha) { uint32_t gpio_data; unsigned long flags; + struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - scsi_qla_host_t *pha = to_qla_parent(ha); + + if (IS_QLA82XX(ha)) + return QLA_SUCCESS; ha->beacon_blink_led = 0; + + if (IS_QLA2031(ha)) + goto set_fw_options; + + if (IS_QLA8031(ha) || IS_QLA81XX(ha)) + return QLA_SUCCESS; + ha->beacon_color_state = QLA_LED_ALL_ON; - ha->isp_ops->beacon_blink(ha); /* Will flip to all off. */ + ha->isp_ops->beacon_blink(vha); /* Will flip to all off. */ /* Give control back to firmware. */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); gpio_data = RD_REG_DWORD(®->gpiod); /* Disable the gpio_data reg for update. */ gpio_data &= ~GPDX_LED_UPDATE_MASK; WRT_REG_DWORD(®->gpiod, gpio_data); RD_REG_DWORD(®->gpiod); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +set_fw_options: ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL; - if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Unable to update fw options (beacon off).\n"); + if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x704d, + "Unable to update fw options (beacon on).\n"); return QLA_FUNCTION_FAILED; } - if (qla2x00_get_fw_options(ha, ha->fw_options) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Unable to get fw options (beacon off).\n"); + if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x704e, + "Unable to update fw options (beacon on).\n"); return QLA_FUNCTION_FAILED; } @@ -1255,7 +1839,7 @@ qla24xx_beacon_off(struct scsi_qla_host *ha) * @ha: HA context */ static void -qla2x00_flash_enable(scsi_qla_host_t *ha) +qla2x00_flash_enable(struct qla_hw_data *ha) { uint16_t data; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -1271,7 +1855,7 @@ qla2x00_flash_enable(scsi_qla_host_t *ha) * @ha: HA context */ static void -qla2x00_flash_disable(scsi_qla_host_t *ha) +qla2x00_flash_disable(struct qla_hw_data *ha) { uint16_t data; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -1292,7 +1876,7 @@ qla2x00_flash_disable(scsi_qla_host_t *ha) * Returns the byte read from flash @addr. */ static uint8_t -qla2x00_read_flash_byte(scsi_qla_host_t *ha, uint32_t addr) +qla2x00_read_flash_byte(struct qla_hw_data *ha, uint32_t addr) { uint16_t data; uint16_t bank_select; @@ -1353,7 +1937,7 @@ qla2x00_read_flash_byte(scsi_qla_host_t *ha, uint32_t addr) * @data: Data to write */ static void -qla2x00_write_flash_byte(scsi_qla_host_t *ha, uint32_t addr, uint8_t data) +qla2x00_write_flash_byte(struct qla_hw_data *ha, uint32_t addr, uint8_t data) { uint16_t bank_select; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -1416,7 +2000,7 @@ qla2x00_write_flash_byte(scsi_qla_host_t *ha, uint32_t addr, uint8_t data) * Returns 0 on success, else non-zero. */ static int -qla2x00_poll_flash(scsi_qla_host_t *ha, uint32_t addr, uint8_t poll_data, +qla2x00_poll_flash(struct qla_hw_data *ha, uint32_t addr, uint8_t poll_data, uint8_t man_id, uint8_t flash_id) { int status; @@ -1456,8 +2040,8 @@ qla2x00_poll_flash(scsi_qla_host_t *ha, uint32_t addr, uint8_t poll_data, * Returns 0 on success, else non-zero. */ static int -qla2x00_program_flash_address(scsi_qla_host_t *ha, uint32_t addr, uint8_t data, - uint8_t man_id, uint8_t flash_id) +qla2x00_program_flash_address(struct qla_hw_data *ha, uint32_t addr, + uint8_t data, uint8_t man_id, uint8_t flash_id) { /* Write Program Command Sequence. */ if (IS_OEM_001(ha)) { @@ -1493,7 +2077,7 @@ qla2x00_program_flash_address(scsi_qla_host_t *ha, uint32_t addr, uint8_t data, * Returns 0 on success, else non-zero. */ static int -qla2x00_erase_flash(scsi_qla_host_t *ha, uint8_t man_id, uint8_t flash_id) +qla2x00_erase_flash(struct qla_hw_data *ha, uint8_t man_id, uint8_t flash_id) { /* Individual Sector Erase Command Sequence */ if (IS_OEM_001(ha)) { @@ -1529,7 +2113,7 @@ qla2x00_erase_flash(scsi_qla_host_t *ha, uint8_t man_id, uint8_t flash_id) * Returns 0 on success, else non-zero. */ static int -qla2x00_erase_flash_sector(scsi_qla_host_t *ha, uint32_t addr, +qla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr, uint32_t sec_mask, uint8_t man_id, uint8_t flash_id) { /* Individual Sector Erase Command Sequence */ @@ -1555,7 +2139,7 @@ qla2x00_erase_flash_sector(scsi_qla_host_t *ha, uint32_t addr, * @flash_id: Flash ID */ static void -qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, +qla2x00_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id, uint8_t *flash_id) { qla2x00_write_flash_byte(ha, 0x5555, 0xaa); @@ -1569,8 +2153,8 @@ qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, } static void -qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr, - uint32_t length) +qla2x00_read_flash_data(struct qla_hw_data *ha, uint8_t *tmp_buf, + uint32_t saddr, uint32_t length) { struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint32_t midpoint, ilength; @@ -1594,20 +2178,20 @@ qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr, } static inline void -qla2x00_suspend_hba(struct scsi_qla_host *ha) +qla2x00_suspend_hba(struct scsi_qla_host *vha) { int cnt; unsigned long flags; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - scsi_qla_host_t *pha = to_qla_parent(ha); /* Suspend HBA. */ - scsi_block_requests(ha->host); + scsi_block_requests(vha->host); ha->isp_ops->disable_intrs(ha); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); /* Pause RISC. */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); RD_REG_WORD(®->hccr); if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) { @@ -1619,30 +2203,33 @@ qla2x00_suspend_hba(struct scsi_qla_host *ha) } else { udelay(10); } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); } static inline void -qla2x00_resume_hba(struct scsi_qla_host *ha) +qla2x00_resume_hba(struct scsi_qla_host *vha) { + struct qla_hw_data *ha = vha->hw; + /* Resume HBA. */ clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - qla2x00_wait_for_hba_online(ha); - scsi_unblock_requests(ha->host); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_chip_reset(vha); + scsi_unblock_requests(vha->host); } uint8_t * -qla2x00_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, +qla2x00_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, uint32_t offset, uint32_t length) { uint32_t addr, midpoint; uint8_t *data; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; /* Suspend HBA. */ - qla2x00_suspend_hba(ha); + qla2x00_suspend_hba(vha); /* Go with read. */ midpoint = ha->optrom_size / 2; @@ -1661,13 +2248,13 @@ qla2x00_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, qla2x00_flash_disable(ha); /* Resume HBA. */ - qla2x00_resume_hba(ha); + qla2x00_resume_hba(vha); return buf; } int -qla2x00_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, +qla2x00_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, uint32_t offset, uint32_t length) { @@ -1675,10 +2262,11 @@ qla2x00_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, uint8_t man_id, flash_id, sec_number, data; uint16_t wd; uint32_t addr, liter, sec_mask, rest_addr; + struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; /* Suspend HBA. */ - qla2x00_suspend_hba(ha); + qla2x00_suspend_hba(vha); rval = QLA_SUCCESS; sec_number = 0; @@ -1878,55 +2466,54 @@ update_flash: qla2x00_flash_disable(ha); /* Resume HBA. */ - qla2x00_resume_hba(ha); + qla2x00_resume_hba(vha); return rval; } uint8_t * -qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, +qla24xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, uint32_t offset, uint32_t length) { + struct qla_hw_data *ha = vha->hw; + /* Suspend HBA. */ - scsi_block_requests(ha->host); + scsi_block_requests(vha->host); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); /* Go with read. */ - qla24xx_read_flash_data(ha, (uint32_t *)buf, offset >> 2, length >> 2); + qla24xx_read_flash_data(vha, (uint32_t *)buf, offset >> 2, length >> 2); /* Resume HBA. */ clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); - scsi_unblock_requests(ha->host); + scsi_unblock_requests(vha->host); return buf; } int -qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, +qla24xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, uint32_t offset, uint32_t length) { int rval; + struct qla_hw_data *ha = vha->hw; /* Suspend HBA. */ - scsi_block_requests(ha->host); + scsi_block_requests(vha->host); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); /* Go with write. */ - rval = qla24xx_write_flash_data(ha, (uint32_t *)buf, offset >> 2, + rval = qla24xx_write_flash_data(vha, (uint32_t *)buf, offset >> 2, length >> 2); - /* Resume HBA -- RISC reset needed. */ clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - qla2x00_wait_for_hba_online(ha); - scsi_unblock_requests(ha->host); + scsi_unblock_requests(vha->host); return rval; } uint8_t * -qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, +qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, uint32_t offset, uint32_t length) { int rval; @@ -1934,19 +2521,22 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, void *optrom; uint8_t *pbuf; uint32_t faddr, left, burst; + struct qla_hw_data *ha = vha->hw; + if (IS_QLA25XX(ha) || IS_QLA81XX(ha)) + goto try_fast; if (offset & 0xfff) goto slow_read; if (length < OPTROM_BURST_SIZE) goto slow_read; +try_fast: optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, &optrom_dma, GFP_KERNEL); if (!optrom) { - qla_printk(KERN_DEBUG, ha, - "Unable to allocate memory for optrom burst read " - "(%x KB).\n", OPTROM_BURST_SIZE / 1024); - + ql_log(ql_log_warn, vha, 0x00cc, + "Unable to allocate memory for optrom burst read (%x KB).\n", + OPTROM_BURST_SIZE / 1024); goto slow_read; } @@ -1958,15 +2548,14 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, if (burst > left) burst = left; - rval = qla2x00_dump_ram(ha, optrom_dma, - flash_data_to_access_addr(faddr), burst); + rval = qla2x00_dump_ram(vha, optrom_dma, + flash_data_addr(ha, faddr), burst); if (rval) { - qla_printk(KERN_WARNING, ha, - "Unable to burst-read optrom segment " - "(%x/%x/%llx).\n", rval, - flash_data_to_access_addr(faddr), + ql_log(ql_log_warn, vha, 0x00f5, + "Unable to burst-read optrom segment (%x/%x/%llx).\n", + rval, flash_data_addr(ha, faddr), (unsigned long long)optrom_dma); - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x00f6, "Reverting to slow-read.\n"); dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, @@ -1987,7 +2576,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, return buf; slow_read: - return qla24xx_read_optrom_data(ha, buf, offset, length); + return qla24xx_read_optrom_data(vha, buf, offset, length); } /** @@ -2009,7 +2598,7 @@ slow_read: * Returns QLA_SUCCESS on successful retrieval of version. */ static void -qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids) +qla2x00_get_fcode_version(struct qla_hw_data *ha, uint32_t pcids) { int ret = QLA_FUNCTION_FAILED; uint32_t istart, iend, iter, vend; @@ -2083,13 +2672,14 @@ qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids) } int -qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) +qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf) { int ret = QLA_SUCCESS; uint8_t code_type, last_image; uint32_t pcihdr, pcids; uint8_t *dbyte; uint16_t *dcode; + struct qla_hw_data *ha = vha->hw; if (!ha->pio_address || !mbuf) return QLA_FUNCTION_FAILED; @@ -2109,8 +2699,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 || qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) { /* No signature */ - DEBUG2(printk("scsi(%ld): No matching ROM " - "signature.\n", ha->host_no)); + ql_log(ql_log_fatal, vha, 0x0050, + "No matching ROM signature.\n"); ret = QLA_FUNCTION_FAILED; break; } @@ -2126,8 +2716,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' || qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') { /* Incorrect header. */ - DEBUG2(printk("%s(): PCI data struct not found " - "pcir_adr=%x.\n", __func__, pcids)); + ql_log(ql_log_fatal, vha, 0x0051, + "PCI data struct not found pcir_adr=%x.\n", pcids); ret = QLA_FUNCTION_FAILED; break; } @@ -2141,8 +2731,9 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) qla2x00_read_flash_byte(ha, pcids + 0x12); ha->bios_revision[1] = qla2x00_read_flash_byte(ha, pcids + 0x13); - DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__, - ha->bios_revision[1], ha->bios_revision[0])); + ql_dbg(ql_dbg_init, vha, 0x0052, + "Read BIOS %d.%d.\n", + ha->bios_revision[1], ha->bios_revision[0]); break; case ROM_CODE_TYPE_FCODE: /* Open Firmware standard for PCI (FCode). */ @@ -2155,12 +2746,14 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) qla2x00_read_flash_byte(ha, pcids + 0x12); ha->efi_revision[1] = qla2x00_read_flash_byte(ha, pcids + 0x13); - DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__, - ha->efi_revision[1], ha->efi_revision[0])); + ql_dbg(ql_dbg_init, vha, 0x0053, + "Read EFI %d.%d.\n", + ha->efi_revision[1], ha->efi_revision[0]); break; default: - DEBUG2(printk("%s(): Unrecognized code type %x at " - "pcids %x.\n", __func__, code_type, pcids)); + ql_log(ql_log_warn, vha, 0x0054, + "Unrecognized code type %x at pcids %x.\n", + code_type, pcids); break; } @@ -2178,23 +2771,30 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) memset(dbyte, 0, 8); dcode = (uint16_t *)dbyte; - qla2x00_read_flash_data(ha, dbyte, FA_RISC_CODE_ADDR * 4 + 10, + qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10, 8); - DEBUG3(printk("%s(%ld): dumping fw ver from flash:\n", - __func__, ha->host_no)); - DEBUG3(qla2x00_dump_buffer((uint8_t *)dbyte, 8)); + ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010a, + "Dumping fw " + "ver from flash:.\n"); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010b, + (uint8_t *)dbyte, 8); if ((dcode[0] == 0xffff && dcode[1] == 0xffff && dcode[2] == 0xffff && dcode[3] == 0xffff) || (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { - DEBUG2(printk("%s(): Unrecognized fw revision at " - "%x.\n", __func__, FA_RISC_CODE_ADDR * 4)); + ql_log(ql_log_warn, vha, 0x0057, + "Unrecognized fw revision at %x.\n", + ha->flt_region_fw * 4); } else { /* values are in big endian */ ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1]; ha->fw_revision[1] = dbyte[2] << 16 | dbyte[3]; ha->fw_revision[2] = dbyte[4] << 16 | dbyte[5]; + ql_dbg(ql_dbg_init, vha, 0x0058, + "FW Version: " + "%d.%d.%d.\n", ha->fw_revision[0], + ha->fw_revision[1], ha->fw_revision[2]); } } @@ -2204,7 +2804,7 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) } int -qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) +qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) { int ret = QLA_SUCCESS; uint32_t pcihdr, pcids; @@ -2212,6 +2812,10 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) uint8_t *bcode; uint8_t code_type, last_image; int i; + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA82XX(ha)) + return ret; if (!mbuf) return QLA_FUNCTION_FAILED; @@ -2224,16 +2828,16 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) dcode = mbuf; /* Begin with first PCI expansion ROM header. */ - pcihdr = 0; + pcihdr = ha->flt_region_boot << 2; last_image = 1; do { /* Verify PCI expansion ROM header. */ - qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20); + qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20); bcode = mbuf + (pcihdr % 4); if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) { /* No signature */ - DEBUG2(printk("scsi(%ld): No matching ROM " - "signature.\n", ha->host_no)); + ql_log(ql_log_fatal, vha, 0x0059, + "No matching ROM signature.\n"); ret = QLA_FUNCTION_FAILED; break; } @@ -2241,15 +2845,15 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) /* Locate PCI data structure. */ pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]); - qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20); + qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20); bcode = mbuf + (pcihdr % 4); /* Validate signature of PCI data structure. */ if (bcode[0x0] != 'P' || bcode[0x1] != 'C' || bcode[0x2] != 'I' || bcode[0x3] != 'R') { /* Incorrect header. */ - DEBUG2(printk("%s(): PCI data struct not found " - "pcir_adr=%x.\n", __func__, pcids)); + ql_log(ql_log_fatal, vha, 0x005a, + "PCI data struct not found pcir_adr=%x.\n", pcids); ret = QLA_FUNCTION_FAILED; break; } @@ -2261,26 +2865,30 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) /* Intel x86, PC-AT compatible. */ ha->bios_revision[0] = bcode[0x12]; ha->bios_revision[1] = bcode[0x13]; - DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__, - ha->bios_revision[1], ha->bios_revision[0])); + ql_dbg(ql_dbg_init, vha, 0x005b, + "Read BIOS %d.%d.\n", + ha->bios_revision[1], ha->bios_revision[0]); break; case ROM_CODE_TYPE_FCODE: /* Open Firmware standard for PCI (FCode). */ ha->fcode_revision[0] = bcode[0x12]; ha->fcode_revision[1] = bcode[0x13]; - DEBUG3(printk("%s(): read FCODE %d.%d.\n", __func__, - ha->fcode_revision[1], ha->fcode_revision[0])); + ql_dbg(ql_dbg_init, vha, 0x005c, + "Read FCODE %d.%d.\n", + ha->fcode_revision[1], ha->fcode_revision[0]); break; case ROM_CODE_TYPE_EFI: /* Extensible Firmware Interface (EFI). */ ha->efi_revision[0] = bcode[0x12]; ha->efi_revision[1] = bcode[0x13]; - DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__, - ha->efi_revision[1], ha->efi_revision[0])); + ql_dbg(ql_dbg_init, vha, 0x005d, + "Read EFI %d.%d.\n", + ha->efi_revision[1], ha->efi_revision[0]); break; default: - DEBUG2(printk("%s(): Unrecognized code type %x at " - "pcids %x.\n", __func__, code_type, pcids)); + ql_log(ql_log_warn, vha, 0x005e, + "Unrecognized code type %x at pcids %x.\n", + code_type, pcids); break; } @@ -2294,7 +2902,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); dcode = mbuf; - qla24xx_read_flash_data(ha, dcode, FA_RISC_CODE_ADDR + 4, 4); + qla24xx_read_flash_data(vha, dcode, ha->flt_region_fw + 4, 4); for (i = 0; i < 4; i++) dcode[i] = be32_to_cpu(dcode[i]); @@ -2302,118 +2910,134 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) || (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { - DEBUG2(printk("%s(): Unrecognized fw version at %x.\n", - __func__, FA_RISC_CODE_ADDR)); + ql_log(ql_log_warn, vha, 0x005f, + "Unrecognized fw revision at %x.\n", + ha->flt_region_fw * 4); } else { ha->fw_revision[0] = dcode[0]; ha->fw_revision[1] = dcode[1]; ha->fw_revision[2] = dcode[2]; ha->fw_revision[3] = dcode[3]; + ql_dbg(ql_dbg_init, vha, 0x0060, + "Firmware revision %d.%d.%d.%d.\n", + ha->fw_revision[0], ha->fw_revision[1], + ha->fw_revision[2], ha->fw_revision[3]); } + /* Check for golden firmware and get version if available */ + if (!IS_QLA81XX(ha)) { + /* Golden firmware is not present in non 81XX adapters */ + return ret; + } + + memset(ha->gold_fw_version, 0, sizeof(ha->gold_fw_version)); + dcode = mbuf; + ha->isp_ops->read_optrom(vha, (uint8_t *)dcode, + ha->flt_region_gold_fw << 2, 32); + + if (dcode[4] == 0xFFFFFFFF && dcode[5] == 0xFFFFFFFF && + dcode[6] == 0xFFFFFFFF && dcode[7] == 0xFFFFFFFF) { + ql_log(ql_log_warn, vha, 0x0056, + "Unrecognized golden fw at 0x%x.\n", + ha->flt_region_gold_fw * 4); + return ret; + } + + for (i = 4; i < 8; i++) + ha->gold_fw_version[i-4] = be32_to_cpu(dcode[i]); + return ret; } static int -qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata) +qla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end) { - uint32_t d[2], faddr; + if (pos >= end || *pos != 0x82) + return 0; - /* Locate first empty entry. */ - for (;;) { - if (ha->hw_event_ptr >= - ha->hw_event_start + FA_HW_EVENT_SIZE) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Log Full!\n")); - return QLA_MEMORY_ALLOC_FAILED; - } + pos += 3 + pos[1]; + if (pos >= end || *pos != 0x90) + return 0; - qla24xx_read_flash_data(ha, d, ha->hw_event_ptr, 2); - faddr = flash_data_to_access_addr(ha->hw_event_ptr); - ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE; - if (d[0] == __constant_cpu_to_le32(0xffffffff) && - d[1] == __constant_cpu_to_le32(0xffffffff)) { - qla24xx_unprotect_flash(ha); + pos += 3 + pos[1]; + if (pos >= end || *pos != 0x78) + return 0; - qla24xx_write_flash_dword(ha, faddr++, - cpu_to_le32(jiffies)); - qla24xx_write_flash_dword(ha, faddr++, 0); - qla24xx_write_flash_dword(ha, faddr++, *fdata++); - qla24xx_write_flash_dword(ha, faddr++, *fdata); - - qla24xx_protect_flash(ha); - break; - } - } - return QLA_SUCCESS; + return 1; } int -qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1, - uint16_t d2, uint16_t d3) +qla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size) { -#define QMARK(a, b, c, d) \ - cpu_to_le32(LSB(a) << 24 | LSB(b) << 16 | LSB(c) << 8 | LSB(d)) + struct qla_hw_data *ha = vha->hw; + uint8_t *pos = ha->vpd; + uint8_t *end = pos + ha->vpd_size; + int len = 0; - int rval; - uint32_t marker[2], fdata[4]; + if (!IS_FWI2_CAPABLE(ha) || !qla2xxx_is_vpd_valid(pos, end)) + return 0; - if (ha->hw_event_start == 0) - return QLA_FUNCTION_FAILED; + while (pos < end && *pos != 0x78) { + len = (*pos == 0x82) ? pos[1] : pos[2]; - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- code=%x, d1=%x, d2=%x, d3=%x.\n", code, d1, d2, d3)); + if (!strncmp(pos, key, strlen(key))) + break; - /* If marker not already found, locate or write. */ - if (!ha->flags.hw_event_marker_found) { - /* Create marker. */ - marker[0] = QMARK('L', ha->fw_major_version, - ha->fw_minor_version, ha->fw_subminor_version); - marker[1] = QMARK(QLA_DRIVER_MAJOR_VER, QLA_DRIVER_MINOR_VER, - QLA_DRIVER_PATCH_VER, QLA_DRIVER_BETA_VER); + if (*pos != 0x90 && *pos != 0x91) + pos += len; - /* Locate marker. */ - ha->hw_event_ptr = ha->hw_event_start; - for (;;) { - qla24xx_read_flash_data(ha, fdata, ha->hw_event_ptr, - 4); - if (fdata[0] == __constant_cpu_to_le32(0xffffffff) && - fdata[1] == __constant_cpu_to_le32(0xffffffff)) - break; - ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE; - if (ha->hw_event_ptr >= - ha->hw_event_start + FA_HW_EVENT_SIZE) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Log Full!\n")); - return QLA_MEMORY_ALLOC_FAILED; - } - if (fdata[2] == marker[0] && fdata[3] == marker[1]) { - ha->flags.hw_event_marker_found = 1; - break; - } - } - /* No marker, write it. */ - if (!ha->flags.hw_event_marker_found) { - rval = qla2xxx_hw_event_store(ha, marker); - if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Failed marker write=%x.!\n", - rval)); - return rval; - } - ha->flags.hw_event_marker_found = 1; - } + pos += 3; } - /* Store error. */ - fdata[0] = cpu_to_le32(code << 16 | d1); - fdata[1] = cpu_to_le32(d2 << 16 | d3); - rval = qla2xxx_hw_event_store(ha, fdata); - if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Failed error write=%x.!\n", - rval)); - } + if (pos < end - len && *pos != 0x78) + return snprintf(str, size, "%.*s", len, pos + 3); - return rval; + return 0; +} + +int +qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha) +{ + int len, max_len; + uint32_t fcp_prio_addr; + struct qla_hw_data *ha = vha->hw; + + if (!ha->fcp_prio_cfg) { + ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); + if (!ha->fcp_prio_cfg) { + ql_log(ql_log_warn, vha, 0x00d5, + "Unable to allocate memory for fcp priorty data (%x).\n", + FCP_PRIO_CFG_SIZE); + return QLA_FUNCTION_FAILED; + } + } + memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE); + + fcp_prio_addr = ha->flt_region_fcp_prio; + + /* first read the fcp priority data header from flash */ + ha->isp_ops->read_optrom(vha, (uint8_t *)ha->fcp_prio_cfg, + fcp_prio_addr << 2, FCP_PRIO_CFG_HDR_SIZE); + + if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 0)) + goto fail; + + /* read remaining FCP CMD config data from flash */ + fcp_prio_addr += (FCP_PRIO_CFG_HDR_SIZE >> 2); + len = ha->fcp_prio_cfg->num_entries * FCP_PRIO_CFG_ENTRY_SIZE; + max_len = FCP_PRIO_CFG_SIZE - FCP_PRIO_CFG_HDR_SIZE; + + ha->isp_ops->read_optrom(vha, (uint8_t *)&ha->fcp_prio_cfg->entry[0], + fcp_prio_addr << 2, (len < max_len ? len : max_len)); + + /* revalidate the entire FCP priority config data, including entries */ + if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 1)) + goto fail; + + ha->flags.fcp_prio_enabled = 1; + return QLA_SUCCESS; +fail: + vfree(ha->fcp_prio_cfg); + ha->fcp_prio_cfg = NULL; + return QLA_FUNCTION_FAILED; } diff --git a/qla2x00t/qla_version.h b/qla2x00t/qla_version.h index 83f4f61bf..44da69fd7 100644 --- a/qla2x00t/qla_version.h +++ b/qla2x00t/qla_version.h @@ -1,15 +1,15 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2011 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.01-k4-tgt" +#define QLA2XXX_VERSION "8.04.00.05.2.2-SCST.03-k-p" #define QLA_DRIVER_MAJOR_VER 8 -#define QLA_DRIVER_MINOR_VER 2 -#define QLA_DRIVER_PATCH_VER 1 +#define QLA_DRIVER_MINOR_VER 4 +#define QLA_DRIVER_PATCH_VER 0 #define QLA_DRIVER_BETA_VER 0 diff --git a/scripts/generate-release-archive b/scripts/generate-release-archive index 3980ac7dd..45a737ac6 100755 --- a/scripts/generate-release-archive +++ b/scripts/generate-release-archive @@ -15,6 +15,9 @@ version="$1"; shift files="$*" if [ -z "$files" ]; then files=$($scriptdir/list-source-files) + if [ -e build_mode ]; then + files="${files} build_mode" + fi fi tar --owner=root --group=root --transform="s|^|$name-$version/|" \ diff --git a/scripts/run-regression-tests b/scripts/run-regression-tests index f792254c4..96b7d74fc 100755 --- a/scripts/run-regression-tests +++ b/scripts/run-regression-tests @@ -278,14 +278,6 @@ CONFIG_TRACING CONFIG_X86_32 \ patch -p1 -f -s <"${p}" >>"${patchoutput}" 2>&1 done fi && - if [ -e $srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ]; then - echo "$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ..." \ - >>"${patchoutput}" - patch -p1 -f -s <$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch \ - >>"${patchoutput}" - else - echo "srpt/patches/kernel-${kver}-pre-cflags.patch not found." - fi && make -s allmodconfig &>"${outputdir}/make-config-output.txt" && for c in $disable; do sed -i.tmp "s/^$c=[ym]\$/$c=n/" .config; done && make -s oldconfig &>/dev/null diff --git a/scripts/specialize-patch b/scripts/specialize-patch index dd0c842f2..f0b0e79ce 100755 --- a/scripts/specialize-patch +++ b/scripts/specialize-patch @@ -227,7 +227,7 @@ function evaluate(stmnt, pattern, arg, op, result) { stmnt = "+#if " (op[2] != 0 ? op[1] : op[2]) } - pattern="^+#if[[:blank:]]*(!*[[:blank:]]*[A-Za-z_]*[[:blank:]]*)\\&\\&[[:blank:]]*([01])[[:blank:]]*$" + pattern="^+#if[[:blank:]]*(!*[[:blank:]]*[A-Za-z_]*)[[:blank:]]*\\&\\&[[:blank:]]*([01])[[:blank:]]*$" while (match(stmnt, pattern, op) != 0) { stmnt = "+#if " (op[2] != 0 ? op[1] : op[2]) @@ -561,6 +561,8 @@ BEGIN { delete_next_blank_line = 0 if (match($0, "^+ *#")) { + if (debug) + printf "/* debug specialize-patch: line %d: %s*/\n", NR, $0 process_preprocessor_statement() } else if (output) diff --git a/scripts/spread-mlx4-ib-interrupts b/scripts/spread-mlx4-ib-interrupts new file mode 100755 index 000000000..6c61766df --- /dev/null +++ b/scripts/spread-mlx4-ib-interrupts @@ -0,0 +1,55 @@ +#!/bin/awk -f + +BEGIN { + if (debug != "") + print "[, ]: "; + "ls -1d /sys/devices/system/node/node* 2>&1 | wc -l" | getline nodes + if (nodes > 1) { + for (i = 0; i < nodes; i++) { + cpus_per_node = 0 + while (("cd /sys/devices/system/cpu && ls -d cpu*/node" i " | sed 's/^cpu//;s,/.*,,'|sort -n" | getline j) > 0) { + if (debug != "") + print "[" i ", " cpus_per_node "]: " j + cpu[i, cpus_per_node++] = j + } + } + } else { + cpus_per_node = 0 + while (("cd /sys/devices/system/cpu && ls -d cpu[0-9]* | sed 's/^cpu//'|sort -n" | getline j) > 0) { + if (debug != "") + print "[0, " cpus_per_node "]: " j + cpu[0, cpus_per_node++] = j + } + } + for (i = 0; i < nodes; i++) + nextcpu[i] = 0 + while (("sed -n 's/.*mlx4-ib-\\([0-9]*\\)-[0-9]*@\\(.*\\)$/\\1 \\2/p' /proc/interrupts | uniq" | getline) > 0) { + port = $1 + bus = substr($0, length($1) + 2) + if (debug != "") + print "HCA port = " port "; bus = " bus + irqcount = 0 + while (("sed -n 's/^[[:blank:]]*\\([0-9]*\\):[0-9[:blank:]]*[^[:blank:]]*[[:blank:]]*\\(mlx4-ib-" port "-[0-9]*@" bus "\\)$/\\1 \\2/p' 0) { + irq[irqcount] = $1 + irqname[irqcount] = substr($0, length($1) + 2) + irqcount++ + } + for (i = 0; i < nodes; i++) { + ch_start = i * irqcount / nodes + ch_end = (i + 1) * irqcount / nodes + for (ch = ch_start; ch < ch_end; ch++) { + c = cpu[i, nextcpu[i]++ % cpus_per_node] + if (nodes > 1) + nodetxt = " (node " i ")" + else + nodetxt = "" + print "IRQ " irq[ch] " (" irqname[ch] "): CPU " c nodetxt + cmd="echo " c " >/proc/irq/" irq[ch] "/smp_affinity_list" + if (debug != "") + print cmd + system(cmd) + } + } + } + exit 0 +} diff --git a/scripts/update-version b/scripts/update-version new file mode 100755 index 000000000..d35df238d --- /dev/null +++ b/scripts/update-version @@ -0,0 +1,27 @@ +#!/bin/sh +# Update the version number in SCST source files. + +usage() { + echo "Usage: $(basename $0) []" + exit 1 +} + +if [ $# != 3 -a $# != 4 ]; then + usage +fi + +major=$1 +minor=$2 +release=$3 +suffix=$4 +fv=$major.$minor.$release$suffix +reldate=$(date "+%d %B %Y") + +sed -i "s,^\(#define[[:blank:]]*\(FT_VERSION\|ISCSI_VERSION_STRING\|Q2T_VERSION_STRING\|SCST_LOCAL_VERSION\|SCST_VERSION_NAME\|DRV_VERSION\|VERSION_STR\)[[:blank:]]*\)\"[^\"]*\",\1\"$major.$minor.$release$suffix\"," scst_local/scst_local.c srpt/src/ib_srpt.c fcst/fcst.h iscsi-scst/include/iscsi_scst_ver.h qla2x00t/qla2x00-target/qla2x00t.h scst/include/scst_const.h +sed -i "s,^\(Version[[:blank:]]*\)[^[:blank:]]*\(\),\1$fv\2," doc/scst_user_spec.sgml +sed -i "s/^Version .*/Version $fv, $reldate/" iscsi-scst/README qla2x00t/qla2x00-target/README scst/README usr/fileio/README +sed -i "s/^\(\(static const char \*scst_local_version_date\|#define[[:blank:]]*DRV_RELDATE\)[[:blank:]]*\)\"[^\"]*\"/\1\"$reldate\"/" scst_local/scst_local.c srpt/src/ib_srpt.c +sed -i "s/^\(#define[[:blank:]]*[^[:blank:]]*_REV[[:blank:]]*\)\"[[:blank:]0-9]*\"/\1\"$(printf "%2d%d%d" $major $minor $release)\"/" usr/fileio/common.h scst/src/dev_handlers/scst_vdisk.c +sed -i "s/^\(#define[[:blank:]]*SCST_VERSION_CODE[[:blank:]]*\).*/\1SCST_VERSION($major, $minor, $release, 0)/" scst/include/scst.h +sed -i "s/^\(#define[[:blank:]]*Q2T_VERSION_CODE[[:blank:]]*\).*/\1Q2T_VERSION($major, $minor, $release, 0)/" qla2x00t/qla2x00-target/qla2x00t.h +sed -i "s/^\(.Version[[:blank:]]*=[[:blank:]]*'SCST Configurator v\)[0-9.]*/\1$fv/" scstadmin/scstadmin.sysfs/scstadmin diff --git a/scst-dkms.spec.in b/scst-dkms.spec.in new file mode 100644 index 000000000..e62d3cb61 --- /dev/null +++ b/scst-dkms.spec.in @@ -0,0 +1,250 @@ +%define kmod_name scst +# kversion: Kernel version as it appears under /lib/modules. +# The algorithm for setting the variable kversion is as follows: +# - If the variable kversion has been set, use its value. +# - If an RPM with the name kernel-headers exists (RHEL / CentOS), use the +# version number of the kernel that package is based on. This provides the +# version number when building on a koji build server. +# - Otherwise use the version number of the running kernel. +%{!?kversion:%define kversion %{expand:%%( + if rpm --quiet -q kernel-headers; then + rpm -q --qf '%%%%{version}-%%%%{release}.%%%%{arch}\\n' \\ + kernel-headers | head -n1; + else + uname -r; + fi + )}} +%{echo:kversion=%{kversion} +} +# kernel_rpm: Name of the kernel RPM if the kernel is available as an RPM. +%if %{expand:%%(rpm --quiet -q kernel-headers || + rpm --quiet -qf /lib/modules/%{kversion}/kernel/arch 2>/dev/null; + echo $((1-$?)))} +%define kernel_rpm %{expand:%%( + if rpm --quiet -q kernel-headers; then + echo kernel; + else + rpm -q --qf '%%%%{name}\\n' \\ + "$(rpm -qf /lib/modules/%%{kversion}/kernel/arch | head -n1)"; + fi + )} +%endif +# krpmver: Version of the kernel RPM. Not necessarily identical to %{kversion}. +%{?kernel_rpm:%define krpmver %{expand:%%( + if rpm --quiet -q %%{kernel_rpm}; then + rpm -q --qf '%%%%{version}-%%%%{release}\\n' "%%{kernel_rpm}"; + else + rpm -q --qf '%%%%{version}-%%%%{release}\\n' kernel-headers; + fi | + head -n1 + )}} +%{echo:krpmver=%{krpmver} +} +# kernel_devel_rpm: Name of the kernel development RPM. +%{?kernel_rpm:%define kernel_devel_rpm %{kernel_rpm}-devel} +# Version of the RPM that is being built. +%define rpm_version @rpm_version@ +# Make command with or without flags. +%define make %{expand:%%(echo ${MAKE:-make})} +%define pkgrel 1 +%define dkms_version %{rpm_version}-%{pkgrel}%{?dist} + +Name: %{kmod_name}-dkms +Version: %{rpm_version} +Release: %{pkgrel}%{?dist} +Summary: SCST mid-layer kernel drivers +Group: System/Kernel +License: GPLv2 +Vendor: http://scst.sourceforge.net/ +URL: http://scst.sourceforge.net/ +# Unfortunately the Red Hat / CentOS kernel-debug-devel RPM provides +# kernel-devel so a workaround is needed to match the kernel-devel RPM. +BuildRequires: %{?kernel_rpm:%{kernel_rpm} = %{krpmver} %{kernel_devel_rpm} = %{krpmver}} gcc make + +Source: %{kmod_name}-%{version}.tar.bz2 +BuildRoot: %{_tmppath}/%{name}-%{version}-build +AutoReqProv: no + +%description +A generic SCSI target subsystem for Linux that allows to convert any Linux +server into a sophisticated storage server. The three layers in SCST are the +target driver layer; the SCSI target core and the device handler layer. SCST +target drivers realize communication with an initiator and implement a storage +protocol like iSCSI, FC or SRP. SCST device handlers implement a SCSI +interface on top of local storage. Examples of such local storage are SCSI +RAID controller (dev_disk handler), block device (vdisk_blockio handler), file +(vdisk_fileio handler) or custom block device behavior implemented in user +space (scst_user). + +Authors: +-------- + Vladislav Bolkhovitin, Bart Van Assche and others + +%package devel +Summary: SCST mid-layer kernel driver development package +Group: Development/Kernel +BuildArch: noarch + +%description devel +A generic SCSI target subsystem for Linux (SCST) that allows to convert +any Linux server into a sophisticated storage server. SCST target drivers +implement protocols like iSCSI, FC or SRP. SCST device handlers either +provide access to a local SCSI RAID controller (dev_disk), block device +(vdisk_blockio), file (vdisk_fileio) or custom block device behavior +implemented in user space (scst_user). + +Authors: +-------- + Vladislav Bolkhovitin, Bart Van Assche and others + +%package userspace +Summary: SCST mid-layer user space software +Group: Development/Kernel +Requires: scst-dkms + +%description userspace +A generic SCSI target subsystem for Linux that allows to convert any Linux +server into a sophisticated storage server. The three layers in SCST are the +target driver layer; the SCSI target core and the device handler layer. SCST +target drivers realize communication with an initiator and implement a storage +protocol like iSCSI, FC or SRP. SCST device handlers implement a SCSI +interface on top of local storage. Examples of such local storage are SCSI +RAID controller (dev_disk handler), block device (vdisk_blockio handler), file +(vdisk_fileio handler) or custom block device behavior implemented in user +space (scst_user). + +%prep + +%setup -q -n %{kmod_name}-%{version} + +%build +export KVER=%{kversion} PREFIX=%{_prefix} +export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y +for d in scst fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do + %{make} -C $d +done + +%install +export KVER=%{kversion} PREFIX=%{_prefix} MANDIR=%{_mandir} +export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y +for d in scst; do + DESTDIR=%{buildroot} %{make} -C $d install +done +for d in fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do + DESTDIR=%{buildroot} INSTALL_MOD_PATH=%{buildroot} %{make} -C $d install +done +rm -f %{buildroot}/lib/modules/%{kversion}/[Mm]odule* +rm -rf %{buildroot}/lib/modules/%{kversion}/extra + +install -d -m 755 %{buildroot}/usr/src/%{kmod_name}-%{dkms_version} +( + cd %{buildroot}/usr/src/%{kmod_name}-%{dkms_version} && + tar --strip-components=1 -xaf %{SOURCE0} +) +cat >%{buildroot}/usr/src/%{kmod_name}-%{dkms_version}/dkms.conf <<"EOF" +PACKAGE_VERSION="%{dkms_version}" +PACKAGE_NAME="%{kmod_name}" +AUTOINSTALL=yes +MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t/qla2x00-target && make -sC scst_local && make -sC srpt && cp */*.ko */*/*.ko */*/*/*.ko ." +CLEAN="make clean" +# Remove any existing ib_srpt.ko kernel modules +PRE_INSTALL="find /lib/modules/${kernelver} -name ib_srpt.ko -exec rm {} \;" +BUILT_MODULE_NAME[ 0]="fcst" +BUILT_MODULE_NAME[ 1]="ib_srpt" +BUILT_MODULE_NAME[ 2]="iscsi-scst" +BUILT_MODULE_NAME[ 3]="qla2x00tgt" +BUILT_MODULE_NAME[ 4]="qla2xxx_scst" +BUILT_MODULE_NAME[ 5]="scst" +BUILT_MODULE_NAME[ 6]="scst_local" +BUILT_MODULE_NAME[ 7]="scst_cdrom" +BUILT_MODULE_NAME[ 8]="scst_changer" +BUILT_MODULE_NAME[ 9]="scst_disk" +BUILT_MODULE_NAME[10]="scst_modisk" +BUILT_MODULE_NAME[11]="scst_processor" +BUILT_MODULE_NAME[12]="scst_raid" +BUILT_MODULE_NAME[13]="scst_tape" +BUILT_MODULE_NAME[14]="scst_user" +BUILT_MODULE_NAME[15]="scst_vdisk" +BUILT_MODULE_NAME[16]="isert-scst" +DEST_MODULE_LOCATION[ 0]="/extra" +DEST_MODULE_LOCATION[ 1]="/extra" +DEST_MODULE_LOCATION[ 2]="/extra" +DEST_MODULE_LOCATION[ 3]="/extra" +DEST_MODULE_LOCATION[ 4]="/extra" +DEST_MODULE_LOCATION[ 5]="/extra" +DEST_MODULE_LOCATION[ 6]="/extra" +DEST_MODULE_LOCATION[ 7]="/extra/dev_handlers" +DEST_MODULE_LOCATION[ 8]="/extra/dev_handlers" +DEST_MODULE_LOCATION[ 9]="/extra/dev_handlers" +DEST_MODULE_LOCATION[10]="/extra/dev_handlers" +DEST_MODULE_LOCATION[11]="/extra/dev_handlers" +DEST_MODULE_LOCATION[12]="/extra/dev_handlers" +DEST_MODULE_LOCATION[13]="/extra/dev_handlers" +DEST_MODULE_LOCATION[14]="/extra/dev_handlers" +DEST_MODULE_LOCATION[15]="/extra/dev_handlers" +DEST_MODULE_LOCATION[16]="/extra" +EOF + +%clean +rm -rf %{buildroot} + +%pre +# Remove any existing ib_srpt.ko kernel modules +find /lib/modules/%{kversion} -name ib_srpt.ko -exec rm {} \; +# Remove files installed by "make install" +rm -f /usr/local/man/man5/iscsi-scstd.conf.5 +rm -f /usr/local/man/man8/iscsi-scst-adm.8 +rm -f /usr/local/man/man8/iscsi-scstd.8 +rm -f /usr/local/sbin/iscsi-scst-adm +rm -f /usr/local/sbin/iscsi-scstd +rm -rf /usr/local/include/scst + +%pre userspace +# Remove files installed by "make install" +rm -f /usr/local/man/man5/iscsi-scstd.conf.5 +rm -f /usr/local/man/man8/iscsi-scst-adm.8 +rm -f /usr/local/man/man8/iscsi-scstd.8 +rm -f /usr/local/sbin/iscsi-scst-adm +rm -f /usr/local/sbin/iscsi-scstd +rm -rf /usr/local/include/scst + +%post +dkms add -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade +dkms build -m %{kmod_name} -v %{dkms_version} +dkms install -m %{kmod_name} -v %{dkms_version} + +%preun +dkms remove -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade --all +true + +%files +%defattr(-,root,root) +/usr/src/%{kmod_name}-%{dkms_version}/ + +%files userspace +%{_mandir}/man5/iscsi-scstd.conf.5.gz +%{_mandir}/man8/iscsi-scst-adm.8.gz +%{_mandir}/man8/iscsi-scstd.8.gz +%{_sbindir}/iscsi-scst-adm +%{_sbindir}/iscsi-scstd +%dir /var/lib/scst/pr +%dir /var/lib/scst/vdev_mode_pages + +%files devel +%defattr(-,root,root) +%dir /usr/include/scst +/usr/include/scst/Module.symvers +/usr/include/scst/scst.h +/usr/include/scst/scst_const.h +/usr/include/scst/scst_debug.h +/usr/include/scst/scst_itf_ver.h +/usr/include/scst/scst_sgv.h +/usr/include/scst/scst_user.h + +%changelog +* Mon Feb 23 2015 Bart Van Assche +- Split spec file into a non-DKMS and a DKMS spec file. +* Fri Jan 16 2015 Bart Van Assche +- Added DKMS support. +* Fri Nov 22 2013 Bart Van Assche +- Initial spec file. diff --git a/scst.spec.in b/scst.spec.in index 1e5786cf0..9f243056f 100644 --- a/scst.spec.in +++ b/scst.spec.in @@ -8,8 +8,8 @@ # - Otherwise use the version number of the running kernel. %{!?kversion:%define kversion %{expand:%%( if rpm --quiet -q kernel-headers; then - rpm -q --qf '%%%%{version}-%%%%{release}.%%%%{arch}' \\ - kernel-headers; + rpm -q --qf '%%%%{version}-%%%%{release}.%%%%{arch}\\n' \\ + kernel-headers | head -n1; else uname -r; fi @@ -47,9 +47,8 @@ # Make command with or without flags. %define make %{expand:%%(echo ${MAKE:-make})} %define pkgrel 1 -%define dkms_version %{rpm_version}-%{pkgrel}%{?dist} -Name: %{kmod_name} +Name: %{kmod_name}-%{kversion} Version: %{rpm_version} Release: %{pkgrel}%{?dist} Summary: SCST mid-layer kernel drivers @@ -57,9 +56,9 @@ Group: System/Kernel License: GPLv2 Vendor: http://scst.sourceforge.net/ URL: http://scst.sourceforge.net/ +%{?kernel_rpm:Requires: %{kernel_rpm} = %{krpmver}} # Unfortunately the Red Hat / CentOS kernel-debug-devel RPM provides # kernel-devel so a workaround is needed to match the kernel-devel RPM. -%{?kernel_rpm:Requires: %{kernel_rpm} = %{krpmver}} BuildRequires: %{?kernel_rpm:%{kernel_rpm} = %{krpmver} %{kernel_devel_rpm} = %{krpmver}} gcc make Source: %{kmod_name}-%{version}.tar.bz2 @@ -81,22 +80,6 @@ Authors: -------- Vladislav Bolkhovitin, Bart Van Assche and others -%package userspace -Summary: SCST mid-layer user space software -Group: Development/Kernel -Requires: scst-dkms - -%description userspace -A generic SCSI target subsystem for Linux that allows to convert any Linux -server into a sophisticated storage server. The three layers in SCST are the -target driver layer; the SCSI target core and the device handler layer. SCST -target drivers realize communication with an initiator and implement a storage -protocol like iSCSI, FC or SRP. SCST device handlers implement a SCSI -interface on top of local storage. Examples of such local storage are SCSI -RAID controller (dev_disk handler), block device (vdisk_blockio handler), file -(vdisk_fileio handler) or custom block device behavior implemented in user -space (scst_user). - %package devel Summary: SCST mid-layer kernel driver development package Group: Development/Kernel @@ -110,25 +93,6 @@ provide access to a local SCSI RAID controller (dev_disk), block device (vdisk_blockio), file (vdisk_fileio) or custom block device behavior implemented in user space (scst_user). -Authors: --------- - Vladislav Bolkhovitin, Bart Van Assche and others - -%package dkms -Summary: DKMS-enabled SCST source code package -Group: System/Kernel -BuildArch: noarch -Requires(pre): dkms gcc make -Requires(post): dkms - -%description dkms -A generic SCSI target subsystem for Linux (SCST) that allows to convert -any Linux server into a sophisticated storage server. SCST target drivers -implement protocols like iSCSI, FC or SRP. SCST device handlers either -provide access to a local SCSI RAID controller (dev_disk), block device -(vdisk_blockio), file (vdisk_fileio) or custom block device behavior -implemented in user space (scst_user). - Authors: -------- Vladislav Bolkhovitin, Bart Van Assche and others @@ -155,55 +119,6 @@ for d in fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do done rm -f %{buildroot}/lib/modules/%{kversion}/[Mm]odule* -install -d -m 755 %{buildroot}/usr/src/%{kmod_name}-%{dkms_version} -( - cd %{buildroot}/usr/src/%{kmod_name}-%{dkms_version} && - tar --strip-components=1 -xaf %{SOURCE0} -) -cat >%{buildroot}/usr/src/%{kmod_name}-%{dkms_version}/dkms.conf <<"EOF" -PACKAGE_VERSION="%{dkms_version}" -PACKAGE_NAME="%{kmod_name}" -AUTOINSTALL=yes -MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t/qla2x00-target && make -sC scst_local && make -sC srpt && cp */*.ko */*/*.ko */*/*/*.ko ." -CLEAN="make clean" -# Remove any existing ib_srpt.ko kernel modules -PRE_INSTALL="find /lib/modules/${kernelver} -name ib_srpt.ko -exec rm {} \;" -BUILT_MODULE_NAME[ 0]="fcst" -BUILT_MODULE_NAME[ 1]="ib_srpt" -BUILT_MODULE_NAME[ 2]="iscsi-scst" -BUILT_MODULE_NAME[ 3]="qla2x00tgt" -BUILT_MODULE_NAME[ 4]="qla2xxx_scst" -BUILT_MODULE_NAME[ 5]="scst" -BUILT_MODULE_NAME[ 6]="scst_local" -BUILT_MODULE_NAME[ 7]="scst_cdrom" -BUILT_MODULE_NAME[ 8]="scst_changer" -BUILT_MODULE_NAME[ 9]="scst_disk" -BUILT_MODULE_NAME[10]="scst_modisk" -BUILT_MODULE_NAME[11]="scst_processor" -BUILT_MODULE_NAME[12]="scst_raid" -BUILT_MODULE_NAME[13]="scst_tape" -BUILT_MODULE_NAME[14]="scst_user" -BUILT_MODULE_NAME[15]="scst_vdisk" -BUILT_MODULE_NAME[16]="isert-scst" -DEST_MODULE_LOCATION[ 0]="/extra" -DEST_MODULE_LOCATION[ 1]="/extra" -DEST_MODULE_LOCATION[ 2]="/extra" -DEST_MODULE_LOCATION[ 3]="/extra" -DEST_MODULE_LOCATION[ 4]="/extra" -DEST_MODULE_LOCATION[ 5]="/extra" -DEST_MODULE_LOCATION[ 6]="/extra" -DEST_MODULE_LOCATION[ 7]="/extra/dev_handlers" -DEST_MODULE_LOCATION[ 8]="/extra/dev_handlers" -DEST_MODULE_LOCATION[ 9]="/extra/dev_handlers" -DEST_MODULE_LOCATION[10]="/extra/dev_handlers" -DEST_MODULE_LOCATION[11]="/extra/dev_handlers" -DEST_MODULE_LOCATION[12]="/extra/dev_handlers" -DEST_MODULE_LOCATION[13]="/extra/dev_handlers" -DEST_MODULE_LOCATION[14]="/extra/dev_handlers" -DEST_MODULE_LOCATION[15]="/extra/dev_handlers" -DEST_MODULE_LOCATION[16]="/extra" -EOF - %clean rm -rf %{buildroot} @@ -217,9 +132,6 @@ rm -f /usr/local/man/man8/iscsi-scstd.8 rm -f /usr/local/sbin/iscsi-scst-adm rm -f /usr/local/sbin/iscsi-scstd rm -rf /usr/local/include/scst - -%pre userspace -# Remove files installed by "make install" rm -f /usr/local/man/man5/iscsi-scstd.conf.5 rm -f /usr/local/man/man8/iscsi-scst-adm.8 rm -f /usr/local/man/man8/iscsi-scstd.8 @@ -230,15 +142,6 @@ rm -rf /usr/local/include/scst %post /sbin/depmod -a %{kversion} -%post dkms -dkms add -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade -dkms build -m %{kmod_name} -v %{dkms_version} -dkms install -m %{kmod_name} -v %{dkms_version} - -%preun dkms -dkms remove -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade --all -true - %files %defattr(-,root,root) %dir /lib/modules/%{kversion}/extra @@ -267,8 +170,6 @@ true %{_sbindir}/iscsi-scstd %dir /var/lib/scst/pr %dir /var/lib/scst/vdev_mode_pages - -%files userspace %{_mandir}/man5/iscsi-scstd.conf.5.gz %{_mandir}/man8/iscsi-scst-adm.8.gz %{_mandir}/man8/iscsi-scstd.8.gz @@ -288,11 +189,9 @@ true /usr/include/scst/scst_sgv.h /usr/include/scst/scst_user.h -%files dkms -%defattr(-,root,root) -/usr/src/%{kmod_name}-%{dkms_version}/ - %changelog +* Mon Feb 23 2015 Bart Van Assche +- Split spec file into a non-DKMS and a DKMS spec file. * Fri Jan 16 2015 Bart Van Assche - Added DKMS support. * Fri Nov 22 2013 Bart Van Assche diff --git a/scst/Makefile b/scst/Makefile index 44cdd1cdd..be6936c9e 100644 --- a/scst/Makefile +++ b/scst/Makefile @@ -1,9 +1,9 @@ # # Common makefile for SCSI target mid-level and its drivers # -# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/scst/README b/scst/README index b7eff48e0..a5d14625b 100644 --- a/scst/README +++ b/scst/README @@ -159,6 +159,23 @@ IMPORTANT: In the current version simultaneous access to local SCSI devices To uninstall, type 'make scst_uninstall'. + +Creating a kernel patch or patched kernel +----------------------------------------- + +You can use generate-kernel-patch or generate-patched-kernel scripts in +the scripts/ subdirectory to convert SCST source tree as it exists +in the Subversion repository to a Linux kernel patch or generate a +kernel source tree with the SCST patches applied correspondingly. This +subdirectory exists only in the SVN tree. + +Example how to use generate-kernel-patch you can find at "How To install +SCST on Ubutuntu 15.04 with in-tree kernel patches" +https://gist.github.com/chrwei/42f8bbb687290b04b598, thanks to Chris Weiss. + +Script rebuild-rhel-kernel-rpm might also be useful in this regard. + + Migration from the obsolete proc interface ------------------------------------------ @@ -331,6 +348,11 @@ in/out in Makefile and scst.h: can clear already measured results by writing 0 in each file. Note, you need a non-preemptible kernel to have correct results. + - CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS - if defined, allows injection + of corrupted DIF tags according to the Oracle specification. This + functionality is working only if dif_mode doesn't contain dev_store + and dif_type is 1. + HIGHMEM kernel configurations are fully supported, but not recommended for performance reasons, except for scst_user, where they are not supported, because this module deals with user supplied memory on a @@ -584,15 +606,25 @@ Every target should have at least the following entries: * 0 - disable black hole mode - * 1 - immediately abort all coming commands + * 1 - immediately abort all coming SCSI commands, i.e. all SCSI commands + are dropped and TM requests return that they completed. It is + supposed to simulate lost front end responses. - * 2 - immediately abort all coming commands and drop all coming TM - commands + * 2 - immediately abort all coming SCSI commands and drop all coming TM + commands. It is supposed to simulate logical target hang, when the + target stops responding, but on the HW/TCP connection level still + appears to be online. - * 3 - immediately abort all coming data transfer commands. + * 3 - immediately abort all coming data transfer SCSI commands, i.e. + only data transfer SCSI commands are dropped, while commands like + INQUIRY and TEST UNIT READY pass well. It is supposed to simulate + flaky front end connectivity, when responses for small commands + pass well, but big data transfers fail. - * 4 - immediately abort all coming data transfer commands and drop all - coming TM commands + * 4 - immediately abort all coming data transfer SCSI commands and + drop all coming TM commands. It is supposed to simulate really + flaky front end connectivity, when TM requests or responses are + also lost. Modes 3 and 4 are the most evil ones, because they are not too well handled by many initiator OS'es, including Linux, so they may never @@ -603,6 +635,17 @@ Every target should have at least the following entries: particular target driver or not, you can find out by checking traces or the target driver's source code. + - dif_capabilities - if this target supports T10-PI, returns which + exact DIF capabilities this target supports. + + - dif_checks_failed - if this target supports T10-PI, returns + statistics how many DIF errors have been detected on the + corresponding processing stages on this target. It returns 3 rows of + numbers with 3 numbers in each row: for target driver stage, for SCST + stage and for dev handler stage. Numbers in each row: how many errors + detected checking application, reference and guard tags + correspondingly. Writing to this attribute resets the numbers. + - cpu_mask - defines CPU affinity mask for threads serving this target. For threads serving LUNs it is used only for devices with threads_pool_type "per_initiator". @@ -639,6 +682,24 @@ Every target should have at least the following entries: until rel_tgt_id becomes unique. This attribute initialized unique by SCST by default. + - forwarding - if set this target is forwarding target, i.e. does not check + any local SCSI events (reservations, etc.). Those event supposed to + be checked on the another, requester's side. + + - *count*, e.g. read_io_count_kb, - statistics about executed + commands and transferred data. Those attributes have speaking names + built from parts: + + 1. Data transfer direction + + 2. Alignment type: not specified or unaligned (on 4K boundaries) + + 3. Type: IO (commands) count or amount of transferred data + + 4. For transferred data: measurement units + + For instance, read_unaligned_cmd_count means number of 4K unaligned IOs. + A target driver may have also the following entries: - "hw_target" - if the target driver supports both hardware and virtual @@ -648,9 +709,8 @@ A target driver may have also the following entries: exist and contain value 1. Subdirectory "sessions" contains one subdirectory for each connected -session with name equal to name of the connected initiator. - -Each session subdirectory contains the following entries: +session with name equal to name of the connected initiator with the +following entries: - initiator_name - contains initiator name @@ -662,9 +722,48 @@ Each session subdirectory contains the following entries: - commands - contains overall number of SCSI commands in this session. + - dif_checks_failed - if target of this session supports T10-PI, returns + statistics how many DIF errors have been detected on the + corresponding processing stages on all DIF-enabled LUNs in this + session. It returns 3 rows of numbers with 3 numbers in each row: for + target driver stage, for SCST stage and for dev handler stage. + Numbers in each row: how many errors detected checking application, + reference and guard tags correspondingly. Writing to this attribute + resets the numbers. Similar statistics returned in attribute with the + same name for each LUN in this session in this LUN's subdirectory, if + its device configured with dif_type > 0. + + - read_cmd_count - number of READ SCSI commands received since beginning + or last reset (writing 0 in this attribute) + + - read_io_count_kb - amount of data in KB read by the initiator since + beginning or last reset (writing 0 in this attribute) + + - write_cmd_count - number of WRITE SCSI commands received since + beginning or last reset (writing 0 in this attribute) + + - write_io_count_kb - amount of data in KB written by the initiator + since beginning or last reset (writing 0 in this attribute) + + - bidi_cmd_count - number of BIDI SCSI commands received since + beginning or last reset (writing 0 in this attribute) + + - bidi_io_count_kb - amount of data in KB transferred by the + initiator since beginning or last reset (writing 0 in this attribute) + + - none_cmd_count - number of not transferring data SCSI commands + (e.g. INQUIRY or TEST UNIT READY) received since beginning or last + reset (writing 0 in this attribute) + + - unknown_cmd_count - number of unknown SCSI commands received since + beginning or last reset (writing 0 in this attribute) + - latency - if CONFIG_SCST_MEASURE_LATENCY enabled, contains latency statistics for this session. + - *count*, e.g. read_io_count_kb, - statistics about executed + commands and transferred data. See above for more details. + - luns - a link pointing out to the corresponding LUNs set (security group) where this session was attached to. @@ -959,6 +1058,49 @@ cache. The following parameters possible for vdisk_fileio: - zero_copy - if set, then this device uses zero copy access to the page cache. At the moment, only read side zero copy is implemented. + - dif_mode - specifies which T10-PI, or DIF, mode this device will use. + See SCSI standards from more info about T10-PI. Available DIF modes + (can be combined using '|'): + + * tgt - DIF tags are checked on the target hardware, if supported + + * scst - DIF tags are checked inside SCST core + + * dev_check - DIF tags are checked inside backend device. No DIF + tags storing is required, but optionally possible. + + * dev_store - DIF tags are stored inside backend device on the WRITE + path and read from it on the READ path. No DIF tags checking is + required, but optionally possible. + + For instance, if only tgt DIF mode specified, then target driver, + serving this device, will inside hardware check, then STRIP DIF tags + from SCSI commands on the WRITE path and generate, then INSERT DIF + tags into SCSI commands on the READ path, so neither SCST core, nor + dev handler will see them. + + Similarly, if only scst DIF mode specified, then target driver will + PASS DIF tags into SCST core, which then check/STRIP/generate/INSERT + them, so dev handler will not see them. + + If only dev_check DIF mode specified, then both target driver and + SCST core will PASS DIF tags into the dev handler, which is then + responsible to check them in the backend hardware. If only dev_store + specified, then DIF tags will only be stored by the dev handler in + the backend hardware without checking at any level. + + If all "tgt|scst|dev_check|dev_store" DIF mode specified, then all + target driver, SCST core and dev handler will check DIF tags, then + dev handler will store them in the backend hardware. + + - dif_type - specifies which DIF SCSI type this device will use. + + - dif_static_app_tag - specifies fixed (static) DIF application tag for + this device. + + - dif_filename - specifies full path to filename, where DIF tags will + be stored. + Handler vdisk_blockio provides BLOCKIO mode to create virtual devices. This mode performs direct block I/O with a block device, bypassing the page cache for all operations. This mode works ideally with high-end @@ -966,10 +1108,10 @@ storage HBAs and for applications that either do not need caching between application and disk or need the large block throughput. See below for more info. -The following common with vdisk_fileio parameters are possible for -vdisk_blockio: filename, blocksize, nv_cache, write_through, read_only, -removable, rotational, thin_provisioned, tst. See vdisk_fileio above for -description of those parameters. +The following parameters possible for vdisk_blockio: filename, +blocksize, nv_cache, read_only, removable, rotational, thin_provisioned, +tst, dif_mode, dif_type, dif_static_app_tag, dif_filename. See +vdisk_fileio above for description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. @@ -2060,6 +2202,31 @@ transfers. As the result, those threads won't receive all the processing power of those CPUs and perform worse. +Commands suspending takes too long +---------------------------------- + +SCST is suspending commands during some management activities like +adding/deleting LUNs or devices. It is done to have lockless LUNs +translation on the hot commands processing path. This brings significant +performance advantage. You will see a message like "Waiting for X active +commands to complete" when this wait started. + +But downside of it is that no new commands start executing until older +ones, which had started before the suspending begun, finished. This +wait can not be any longer, than the worst command latency any your +initiator is seeing at this particular time. + +So, if this wait takes too long, in majority of cases it means that you +are overloading your storage. A proper storage should have worst case +latency below few hundreds of milliseconds. In this case the SCST +suspending will finish in few hundreds of milliseconds at worse. + +Another case, when it can take too long to suspend is a hung user space +device (i.e. scst_user device) not responding to any command. In this +case you should kill the corresponding user space program to finish +suspending. + + Work if target's backstorage or link is too slow ------------------------------------------------ diff --git a/scst/README_in-tree b/scst/README_in-tree index 553fac3e6..386eb7c73 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -233,6 +233,11 @@ your favorite kernel configuration Makefile target, e.g. "make xconfig": can clear already measured results by writing 0 in each file. Note, you need a non-preemptible kernel to have correct results. + - CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS - if defined, allows injection + of corrupted DIF tags according to the Oracle specification. This + functionality is working only if dif_mode doesn't contain dev_store + and dif_type is 1. + HIGHMEM kernel configurations are fully supported, but not recommended for performance reasons. @@ -460,15 +465,25 @@ Every target should have at least the following entries: * 0 - disable black hole mode - * 1 - immediately abort all coming commands + * 1 - immediately abort all coming SCSI commands, i.e. all SCSI commands + are dropped and TM requests return that they completed. It is + supposed to simulate lost front end responses. - * 2 - immediately abort all coming commands and drop all coming TM - commands + * 2 - immediately abort all coming SCSI commands and drop all coming TM + commands. It is supposed to simulate logical target hang, when the + target stops responding, but on the HW/TCP connection level still + appears to be online. - * 3 - immediately abort all coming data transfer commands. + * 3 - immediately abort all coming data transfer SCSI commands, i.e. + only data transfer SCSI commands are dropped, while commands like + INQUIRY and TEST UNIT READY pass well. It is supposed to simulate + flaky front end connectivity, when responses for small commands + pass well, but big data transfers fail. - * 4 - immediately abort all coming data transfer commands and drop all - coming TM commands + * 4 - immediately abort all coming data transfer SCSI commands and + drop all coming TM commands. It is supposed to simulate really + flaky front end connectivity, when TM requests or responses are + also lost. Modes 3 and 4 are the most evil ones, because they are not too well handled by many initiator OS'es, including Linux, so they may never @@ -479,6 +494,17 @@ Every target should have at least the following entries: particular target driver or not, you can find out by checking traces or the target driver's source code. + - dif_capabilities - if this target supports T10-PI, returns which + exact DIF capabilities this target supports. + + - dif_checks_failed - if this target supports T10-PI, returns + statistics how many DIF errors have been detected on the + corresponding processing stages on this target. It returns 3 rows of + numbers with 3 numbers in each row: for target driver stage, for SCST + stage and for dev handler stage. Numbers in each row: how many errors + detected checking application, reference and guard tags + correspondingly. Writing to this attribute resets the numbers. + - cpu_mask - defines CPU affinity mask for threads serving this target. For threads serving LUNs it is used only for devices with threads_pool_type "per_initiator". @@ -515,6 +541,24 @@ Every target should have at least the following entries: until rel_tgt_id becomes unique. This attribute initialized unique by SCST by default. + - forwarding - if set this target is forwarding target, i.e. does not check + any local SCSI events (reservations, etc.). Those event supposed to + be checked on the another, requester's side. + + - *count*, e.g. read_io_count_kb, - statistics about executed + commands and transferred data. Those attributes have speaking names + built from parts: + + 1. Data transfer direction + + 2. Alignment type: not specified or unaligned (on 4K boundaries) + + 3. Type: IO (commands) count or amount of transferred data + + 4. For transferred data: measurement units + + For instance, read_unaligned_cmd_count means number of 4K unaligned IOs. + A target driver may have also the following entries: - "hw_target" - if the target driver supports both hardware and virtual @@ -524,9 +568,8 @@ A target driver may have also the following entries: exist and contain value 1. Subdirectory "sessions" contains one subdirectory for each connected -session with name equal to name of the connected initiator. - -Each session subdirectory contains the following entries: +session with name equal to name of the connected initiator with the +following entries: - initiator_name - contains initiator name @@ -538,9 +581,48 @@ Each session subdirectory contains the following entries: - commands - contains overall number of SCSI commands in this session. + - dif_checks_failed - if target of this session supports T10-PI, returns + statistics how many DIF errors have been detected on the + corresponding processing stages on all DIF-enabled LUNs in this + session. It returns 3 rows of numbers with 3 numbers in each row: for + target driver stage, for SCST stage and for dev handler stage. + Numbers in each row: how many errors detected checking application, + reference and guard tags correspondingly. Writing to this attribute + resets the numbers. Similar statistics returned in attribute with the + same name for each LUN in this session in this LUN's subdirectory, if + its device configured with dif_type > 0. + + - read_cmd_count - number of READ SCSI commands received since beginning + or last reset (writing 0 in this attribute) + + - read_io_count_kb - amount of data in KB read by the initiator since + beginning or last reset (writing 0 in this attribute) + + - write_cmd_count - number of WRITE SCSI commands received since + beginning or last reset (writing 0 in this attribute) + + - write_io_count_kb - amount of data in KB written by the initiator + since beginning or last reset (writing 0 in this attribute) + + - bidi_cmd_count - number of BIDI SCSI commands received since + beginning or last reset (writing 0 in this attribute) + + - bidi_io_count_kb - amount of data in KB transferred by the + initiator since beginning or last reset (writing 0 in this attribute) + + - none_cmd_count - number of not transferring data SCSI commands + (e.g. INQUIRY or TEST UNIT READY) received since beginning or last + reset (writing 0 in this attribute) + + - unknown_cmd_count - number of unknown SCSI commands received since + beginning or last reset (writing 0 in this attribute) + - latency - if CONFIG_SCST_MEASURE_LATENCY enabled, contains latency statistics for this session. + - *count*, e.g. read_io_count_kb, - statistics about executed + commands and transferred data. See above for more details. + - luns - a link pointing out to the corresponding LUNs set (security group) where this session was attached to. @@ -831,6 +913,49 @@ cache. The following parameters possible for vdisk_fileio: - zero_copy - if set, then this device uses zero copy access to the page cache. At the moment, only read side zero copy is implemented. + - dif_mode - specifies which T10-PI, or DIF, mode this device will use. + See SCSI standards from more info about T10-PI. Available DIF modes + (can be combined using '|'): + + * tgt - DIF tags are checked on the target hardware, if supported + + * scst - DIF tags are checked inside SCST core + + * dev_check - DIF tags are checked inside backend device. No DIF + tags storing is required, but optionally possible. + + * dev_store - DIF tags are stored inside backend device on the WRITE + path and read from it on the READ path. No DIF tags checking is + required, but optionally possible. + + For instance, if only tgt DIF mode specified, then target driver, + serving this device, will inside hardware check, then STRIP DIF tags + from SCSI commands on the WRITE path and generate, then INSERT DIF + tags into SCSI commands on the READ path, so neither SCST core, nor + dev handler will see them. + + Similarly, if only scst DIF mode specified, then target driver will + PASS DIF tags into SCST core, which then check/STRIP/generate/INSERT + them, so dev handler will not see them. + + If only dev_check DIF mode specified, then both target driver and + SCST core will PASS DIF tags into the dev handler, which is then + responsible to check them in the backend hardware. If only dev_store + specified, then DIF tags will only be stored by the dev handler in + the backend hardware without checking at any level. + + If all "tgt|scst|dev_check|dev_store" DIF mode specified, then all + target driver, SCST core and dev handler will check DIF tags, then + dev handler will store them in the backend hardware. + + - dif_type - specifies which DIF SCSI type this device will use. + + - dif_static_app_tag - specifies fixed (static) DIF application tag for + this device. + + - dif_filename - specifies full path to filename, where DIF tags will + be stored. + Handler vdisk_blockio provides BLOCKIO mode to create virtual devices. This mode performs direct block I/O with a block device, bypassing the page cache for all operations. This mode works ideally with high-end @@ -838,10 +963,10 @@ storage HBAs and for applications that either do not need caching between application and disk or need the large block throughput. See below for more info. -The following common with vdisk_fileio parameters are possible for -vdisk_blockio: filename, blocksize, nv_cache, write_through, read_only, -removable, rotational, thin_provisioned, tst. See vdisk_fileio above for -description of those parameters. +The following parameters possible for vdisk_blockio: filename, +blocksize, nv_cache, read_only, removable, rotational, thin_provisioned, +tst, dif_mode, dif_type, dif_static_app_tag, dif_filename. See +vdisk_fileio above for description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. @@ -1556,6 +1681,31 @@ transfers. As the result, those threads won't receive all the processing power of those CPUs and perform worse. +Commands suspending takes too long +---------------------------------- + +SCST is suspending commands during some management activities like +adding/deleting LUNs or devices. It is done to have lockless LUNs +translation on the hot commands processing path. This brings significant +performance advantage. You will see a message like "Waiting for X active +commands to complete" when this wait started. + +But downside of it is that no new commands start executing until older +ones, which had started before the suspending begun, finished. This +wait can not be any longer, than the worst command latency any your +initiator is seeing at this particular time. + +So, if this wait takes too long, in majority of cases it means that you +are overloading your storage. A proper storage should have worst case +latency below few hundreds of milliseconds. In this case the SCST +suspending will finish in few hundreds of milliseconds at worse. + +Another case, when it can take too long to suspend is a hung user space +device (i.e. scst_user device) not responding to any command. In this +case you should kill the corresponding user space program to finish +suspending. + + Work if target's backstorage or link is too slow ------------------------------------------------ diff --git a/scst/T10-PI b/scst/T10-PI new file mode 100644 index 000000000..28c0c8a4c --- /dev/null +++ b/scst/T10-PI @@ -0,0 +1,141 @@ +GENERAL CONSIDERATIONS +====================== + +SCST has almost full features implementation of T10-PI standard +supporting all protection modes. The only missing feature is Application +Tag mode page. A patch implementing it is welcome. + +SCST T10-PI implementation has 2 modes of operations: + +1. Computing. In this mode no protection information (PI) is stored. It +only checked upon arrival (WRITEs) and calculated from the data upon +sending (READs). Goal of this mode to provide protection against uplink, +i.e. path to initiator, corruptions. + +2. Storing. In this mode PI is stored either in a file/device as simple +array with 8 bytes entries, or inside block device using block integrity +extensions. In this mode full end-to-end protection could be +implemented. + +In both "computing" and "storing" modes PI can be checked on any stages +of commands processing: + +1. Target HW + +2. SCST + +3. Backend HW, if it supports block integrity extensions. + +Any of them or all can be independently enabled or disabled depending +from level of performance overhead and required protection. For +instance, the target HW stage is the cheapest from performance +perspective, but doesn't protect against corruptions or misdirections on +PCI bus between the target HW and system. Another example, if backend HW +supports T10-PI, no checks on other stages are generally needed, but can +be used to localize place where corruption happened. Checking on each +stage to be able to localize corruption place is the recommended by T10 +way of processing. + + +USER INTERFACE +============== + +Currently only scst_vdisk handler supports T10-PI. + +See in the main README description of SCST sysfs attributes: +dif_capabilities, dif_checks_failed, dif_mode, dif_type, +dif_static_app_tag and dif_filename. + +Examples: +--------- + +dif_mode=tgt, dif_type=1 - TGT level only check, type 1 + +dif_mode=scst, dif_type=1, dif_static_app_tag=0x4149 - + SCST level only check, type 1, static app tag 0x4149 + +dif_mode=scst|dev_store, dif_type=1, dif_filename=/var/lib/scst/dif_tags/rd1_dif.dif - + SCST level only check and storing PI data in the file + +dif_mode=scst, dif_type=2 - SCST level only check, type 2 (32-byte commands) + +dif_mode=tag|scst|dev_check|dev_store, dif_type=1 - all levels check + storing tags using block integrity extensions, type 1 + + +INTERFACE BETWEEN SCST AND TARGET DRIVERS +========================================= + +To support T10-PI struct scst_tgt_template has several new fields: +supported_dif_block_sizes, dif_supported, hw_dif_type1_supported, +hw_dif_type2_supported, hw_dif_type3_supported, hw_dif_ip_supported, +hw_dif_same_sg_layout_required. See their description in their comments +in scst.h. + +A READ-direction workflow from a target driver perspective should look +like: + +1. No extra actions until xmit_response() stage* + +2. On xmit_response() stage call scst_cmd_get_dif_sg(cmd)**. If returned +value is NULL, no further PI actions are needed. + +3. Otherwise, call scst_get_read_dif_tgt_actions(cmd) to find out what +PI actions are needed. The return value has encoded 2 types of actions: +action itself and tags checks. + +Possible actions, which could be extracted by using scst_get_dif_action(), +are: + + - SCST_DIF_ACTION_STRIP - check, then strip the protection info, if it + is OK + + - SCST_DIF_ACTION_INSERT - insert the protection info + + - SCST_DIF_ACTION_PASS_CHECK - check the protection info, then pass it + to the initiator + + - SCST_DIF_ACTION_PASS - then pass the protection information to the + initiator without checking + +Possible tags checks, which could be extracted by using +scst_get_dif_checks() are: + + - SCST_DIF_CHECK_GUARD_TAG - check the guard tags + + - SCST_DIF_CHECK_REF_TAG - check the reference tags + + - SCST_DIF_CHECK_APP_TAG - check the application tags comparing them to + the static tag set by dif_static_app_tag attribute + +4. Perform the requested PI actions and transfer PI from the returned +dif_sg together with the regular data**. + + +A WRITE-direction workflow from a target driver perspective should look +like: + +1. No extra actions until rdy_to_xfer() stage* + +2. On rdy_to_xfer() stage call scst_cmd_get_dif_sg(cmd)**. If returned +value is NULL, no further PI actions are needed. + +3. Otherwise, call scst_get_write_dif_tgt_actions(cmd) to find out what +PI actions are needed. + +4. Perform the requested PI actions and transfer PI to the returned +dif_sg together with regular data**. + + +(*) If the target driver uses custom data buffer, on this stage it must +together with data sg by scst_cmd_set_tgt_dif_sg() function also set the +corresponding PI sg. + +(**) The PI SG returned by scst_cmd_get_dif_sg() does not have the +corresponding SG-segments counter, because it is strictly bound to +number of blocks in the data SG. + + +You can find full external SCST T10-PI interface if you look in scst.h +functions with "_dif_" string in their name. They are well documented in +comments near them. diff --git a/scst/include/scst.h b/scst/include/scst.h index 51b84bc59..dc6273dc7 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -1,9 +1,9 @@ /* * include/scst.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * Copyright (C) 2010 - 2011 Bart Van Assche . * * Main SCSI target mid-level include file. @@ -27,6 +27,9 @@ /* #define CONFIG_SCST_DEBUG_TM */ /* #define CONFIG_SCST_TM_DBG_GO_OFFLINE */ +/** See README for description of those conditional defines **/ +#define CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS + #include #ifndef INSIDE_KERNEL_TREE #include @@ -275,6 +278,17 @@ static inline void hex2bin(u8 *dst, const char *src, size_t count) } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) +/* + * See also patch "new helper: file_inode(file)" (commit ID + * 496ad9aa8ef448058e36ca7a787c61f2e63f0f54). + */ +static inline struct inode *file_inode(const struct file *f) +{ + return f->f_path.dentry->d_inode; +} +#endif + #ifndef __list_for_each /* ToDo: cleanup when both are the same for all relevant kernels */ #define __list_for_each list_for_each @@ -688,6 +702,9 @@ enum scst_exec_context { /* Cache of acg->acg_black_hole_type */ #define SCST_TGT_DEV_BLACK_HOLE 1 +/* Cache of tgt->tgt_forwarding */ +#define SCST_TGT_DEV_FORWARDING 5 + /************************************************************* ** I/O grouping types. Changing them don't forget to change ** the corresponding *_STR values in scst_const.h! @@ -739,6 +756,161 @@ enum scst_exec_context { SCST_SENSE_ASC_VALID | \ SCST_SENSE_ASCQ_VALID) +/************************************************************* + ** T10-PI (DIF) support + *************************************************************/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) +struct t10_pi_tuple { + __be16 guard_tag; + __be16 app_tag; + __be32 ref_tag; +}; +#endif + +/* + * Defines where and how to deal with DIF tags. Can be OR'ed to get + * multilevel checks + */ +enum scst_dif_mode { + SCST_DIF_MODE_NONE = 0, + + /* STRIP/INSERT/CHECK inside target HW */ + SCST_DIF_MODE_TGT = 1, + + /* SCST checking or creating tags on the fly */ + SCST_DIF_MODE_SCST = 2, + + /* Backend device checking tags */ + SCST_DIF_MODE_DEV_CHECK = 4, + + /* Backend device storing tags, creating them on writes, if needed */ + SCST_DIF_MODE_DEV_STORE = 8, +}; +#define SCST_DIF_MODE_DEV (SCST_DIF_MODE_DEV_CHECK|SCST_DIF_MODE_DEV_STORE) + +#define SCST_DIF_MODE_TGT_STR "tgt" +#define SCST_DIF_MODE_SCST_STR "scst" +#define SCST_DIF_MODE_DEV_CHECK_STR "dev_check" +#define SCST_DIF_MODE_DEV_STORE_STR "dev_store" + +/* + * T10-PI actions. + * + * Don't use masks and shifts directly! Use helper functions instead! + */ +enum scst_dif_actions { + SCST_DIF_ACTION_NONE = 0, /* do nothing */ + +#define SCST_DIF_CHECKS_SHIFT 0 + /* OR'able tags to check */ + SCST_DIF_CHECK_GUARD_TAG = 1, + SCST_DIF_CHECK_APP_TAG = 2, + SCST_DIF_CHECK_REF_TAG = 4, +#define SCST_DIF_CHECKS_MASK (SCST_DIF_CHECK_GUARD_TAG|SCST_DIF_CHECK_APP_TAG| \ + SCST_DIF_CHECK_REF_TAG) + +#define SCST_DIF_ACTION_SHIFT 4 + /* How to handle PI */ + SCST_DIF_ACTION_STRIP = (1 << SCST_DIF_ACTION_SHIFT), /* check then strip, if OK */ + SCST_DIF_ACTION_INSERT = (2 << SCST_DIF_ACTION_SHIFT), + SCST_DIF_ACTION_PASS_CHECK = (3 << SCST_DIF_ACTION_SHIFT), /* check then pass, if OK */ + SCST_DIF_ACTION_PASS = (4 << SCST_DIF_ACTION_SHIFT), /* just pass, no check */ + +#define SCST_DIF_ACTION_MASK (SCST_DIF_ACTION_STRIP|SCST_DIF_ACTION_INSERT| \ + SCST_DIF_ACTION_PASS_CHECK|SCST_DIF_ACTION_PASS) +}; + +/* Shift for target driver DIF actions */ +#define SCST_DIF_TGT_ACTION_SHIFT 0 + +/* Shift for SCST DIF actions */ +#define SCST_DIF_SCST_ACTION_SHIFT (SCST_DIF_TGT_ACTION_SHIFT+4) + +/* Shift for dev handler DIF actions */ +#define SCST_DIF_DEV_ACTION_SHIFT (SCST_DIF_SCST_ACTION_SHIFT+4) + +/* Returns DIF checks by stripping the action part from a */ +static inline enum scst_dif_actions scst_get_dif_checks(enum scst_dif_actions a) +{ + return (a & SCST_DIF_CHECKS_MASK); +} + +/* Sets the DIF checks part in a */ +static inline void scst_set_dif_checks(enum scst_dif_actions *a, + enum scst_dif_actions checks) +{ + checks &= ~SCST_DIF_CHECKS_MASK; + *a |= checks << SCST_DIF_CHECKS_SHIFT; + return; +} + +/* Returns DIF action by stripping the checks part from a */ +static inline enum scst_dif_actions scst_get_dif_action(enum scst_dif_actions a) +{ + return (a & SCST_DIF_ACTION_MASK); +} + +/* Sets the DIF action part in a */ +static inline void scst_set_dif_action(enum scst_dif_actions *a, + enum scst_dif_actions action) +{ + action &= ~SCST_DIF_ACTION_MASK; + *a |= action; + return; +} + +/* Returns TGT DIF actions from a, including checks */ +static inline enum scst_dif_actions scst_get_tgt_dif_actions(enum scst_dif_actions a) +{ + BUILD_BUG_ON(SCST_DIF_CHECKS_SHIFT != 0); + return ((a >> SCST_DIF_TGT_ACTION_SHIFT) & SCST_DIF_ACTION_MASK) | + (a & SCST_DIF_CHECKS_MASK); +} + +/* Sets TGT DIF action in a. DIF checks in a are not affected by this function. */ +static inline void scst_set_tgt_dif_action(enum scst_dif_actions *a, + enum scst_dif_actions tgt_a) +{ + tgt_a &= SCST_DIF_ACTION_MASK; + *a |= tgt_a << SCST_DIF_TGT_ACTION_SHIFT; + return; +} + +/* Returns SCST DIF actions from a, including checks */ +static inline enum scst_dif_actions scst_get_scst_dif_actions(enum scst_dif_actions a) +{ + BUILD_BUG_ON(SCST_DIF_CHECKS_SHIFT != 0); + return ((a >> SCST_DIF_SCST_ACTION_SHIFT) & SCST_DIF_ACTION_MASK) | + (a & SCST_DIF_CHECKS_MASK); +} + +/* Sets SCST DIF action in a. DIF checks in a are not affected by this function. */ +static inline void scst_set_scst_dif_action(enum scst_dif_actions *a, + enum scst_dif_actions scst_a) +{ + scst_a &= SCST_DIF_ACTION_MASK; + *a |= scst_a << SCST_DIF_SCST_ACTION_SHIFT; + return; +} + +/* Returns DEV DIF actions from a, including checks */ +static inline enum scst_dif_actions scst_get_dev_dif_actions(enum scst_dif_actions a) +{ + BUILD_BUG_ON(SCST_DIF_CHECKS_SHIFT != 0); + return ((a >> SCST_DIF_DEV_ACTION_SHIFT) & SCST_DIF_ACTION_MASK) | + (a & SCST_DIF_CHECKS_MASK); +} + +/* Sets SCST DIF action in a. DIF checks in a are not affected by this function. */ +static inline void scst_set_dev_dif_action(enum scst_dif_actions *a, + enum scst_dif_actions dev_a) +{ + dev_a &= SCST_DIF_ACTION_MASK; + *a |= dev_a << SCST_DIF_DEV_ACTION_SHIFT; + return; +} + /************************************************************* * TYPES *************************************************************/ @@ -774,6 +946,33 @@ typedef enum dma_data_direction scst_data_direction; struct scst_tgt_template { /* public: */ + /* + * If not NULL, points to 0-terminated array of integers listing all + * supported for T10-PI DIF-enabled devices block sizes. SCST will not + * allow to add DIF-enabled devices with another block sizes to this + * target driver's initiator groups. + * + * Can be overiden by scst_tgt_set_supported_dif_block_sizes() + * + * OPTIONAL + */ + const int *const supported_dif_block_sizes; + + /* + * Preferred SCSI LUN addressing method. + */ + enum scst_lun_addr_method preferred_addr_method; + + /* + * The maximum time in seconds cmd can stay inside the target + * hardware, i.e. after rdy_to_xfer() and xmit_response(), before + * on_hw_pending_cmd_timeout() will be called, if defined. + * + * In the current implementation a cmd will be aborted in time t + * max_hw_pending_time <= t < 2*max_hw_pending_time. + */ + int max_hw_pending_time; + /* * SG tablesize allows to check whether scatter/gather can be used * or not. @@ -825,19 +1024,62 @@ struct scst_tgt_template { unsigned multithreaded_init_done:1; /* - * Preferred SCSI LUN addressing method. + * True, if this target driver supports T10-PI (DIF), i.e. sending and + * receiving DIF PI tags. If false, SCST will not allow to add + * DIF-enabled devices to this target driver's initiator groups. + * + * Can be overriden per target by scst_tgt_set_dif_supported() */ - enum scst_lun_addr_method preferred_addr_method; + unsigned dif_supported:1; /* - * The maximum time in seconds cmd can stay inside the target - * hardware, i.e. after rdy_to_xfer() and xmit_response(), before - * on_hw_pending_cmd_timeout() will be called, if defined. + * True, if this target driver supports T10-PI (DIF), i.e. INSERT/STRIPE + * mode, in hardware for type 1 protection. If false, SCST will not + * allow to add type 1 devices with TGT DIF mode to this target driver's + * initiator groups. * - * In the current implementation a cmd will be aborted in time t - * max_hw_pending_time <= t < 2*max_hw_pending_time. + * Can be overriden per target by scst_tgt_set_hw_dif_type1_supported() */ - int max_hw_pending_time; + unsigned hw_dif_type1_supported:1; + + /* + * True, if this target driver supports T10-PI (DIF), i.e. INSERT/STRIPE + * mode, in hardware for type 2 protection. If false, SCST will not + * allow to add type 2 devices with TGT DIF mode to this target driver's + * initiator groups. + * + * Can be overriden per target by scst_tgt_set_hw_dif_type2_supported() + */ + unsigned hw_dif_type2_supported:1; + + /* + * True, if this target driver supports T10-PI (DIF), i.e. INSERT/STRIPE + * mode, in hardware for type 3 protection. If false, SCST will not + * allow to add type 3 devices with TGT DIF mode to this target driver's + * initiator groups. + * + * Can be overriden per target by scst_tgt_set_hw_dif_type3_supported() + */ + unsigned hw_dif_type3_supported:1; + + /* + * True, if this target driver supports IP checksum format of T10-PI + * (DIF) guard tags, i.e. hardware translation of guard tags between IP + * and CRC on SCST/target driver boundary for better performance, + * because IP checksums are much cheaper for CPU, than CRC. + * + * Can be overriden per target by scst_tgt_set_hw_dif_ip_supported() + */ + unsigned hw_dif_ip_supported:1; + + /* + * True, if this target driver requires the same layout for both data + * and DIF tags SG vectors. Otherwise, for DIF tags minimally possible + * SG vector size will be allocated. + * + * Can be overriden per target by scst_tgt_set_hw_dif_same_sg_layout_required() + */ + unsigned hw_dif_same_sg_layout_required:1; /* * This function is equivalent to the SCSI @@ -1032,7 +1274,7 @@ struct scst_tgt_template { * A negative value should be returned whenever there is * an error. * - * MUST HAVE + * OBSOLETE */ int (*detect)(struct scst_tgt_template *tgt_template); @@ -1689,12 +1931,29 @@ struct scst_tgt { struct list_head tgt_acg_list; /* target ACG groups */ #endif + /* + * Set, if this target is forwarding target, i.e. does not check + * any local SCSI events (reservations, etc.). Those event supposed + * to be checked on the another, requester's side. + */ + unsigned tgt_forwarding:1; + + /* Per target analog of the corresponding driver's fields */ + unsigned tgt_dif_supported:1; + unsigned tgt_hw_dif_type1_supported:1; + unsigned tgt_hw_dif_type2_supported:1; + unsigned tgt_hw_dif_type3_supported:1; + unsigned tgt_hw_dif_ip_supported:1; + unsigned tgt_hw_dif_same_sg_layout_required:1; + /* * Maximum SG table size. Needed here, since different cards on the * same target template can have different SG table limitations. */ int sg_tablesize; + const int *tgt_supported_dif_block_sizes; + /* Used for storage of target driver private stuff */ void *tgt_priv; @@ -1705,10 +1964,10 @@ struct scst_tgt { * They protected by tgt_lock, where necessary. */ bool retry_timer_active; - struct timer_list retry_timer; int retry_cmds; - spinlock_t tgt_lock; + struct timer_list retry_timer; struct list_head retry_cmd_list; + spinlock_t tgt_lock; /* Used to wait until session finished to unregister */ wait_queue_head_t unreg_waitQ; @@ -1726,6 +1985,11 @@ struct scst_tgt { uint16_t rel_tgt_id; + /* How many DIF failures detected on this target on the corresponding stage */ + atomic_t tgt_dif_app_failed_tgt, tgt_dif_ref_failed_tgt, tgt_dif_guard_failed_tgt; + atomic_t tgt_dif_app_failed_scst, tgt_dif_ref_failed_scst, tgt_dif_guard_failed_scst; + atomic_t tgt_dif_app_failed_dev, tgt_dif_ref_failed_dev, tgt_dif_guard_failed_dev; + #ifdef CONFIG_SCST_PROC /* Name of the default security group ("Default_target_name") */ char *default_group_name; @@ -1788,6 +2052,7 @@ struct scst_ext_latency_stat { struct scst_io_stat_entry { uint64_t cmd_count; uint64_t io_byte_count; + uint64_t unaligned_cmd_count; }; /* @@ -2058,6 +2323,9 @@ struct scst_cmd { /* Set if this command was sent in double UA possible state */ unsigned int double_ua_possible:1; + /* Set if DIF check for just read data was deferred to thread context */ + unsigned int deferred_dif_read_check:1; + /* Set if this command contains status */ unsigned int is_send_status:1; @@ -2088,19 +2356,28 @@ struct scst_cmd { /* * Set by SCST if the custom data buffer allocated by the target driver - * or, for internal commands, by SCST core . + * or, for internal commands, by SCST core. */ unsigned int tgt_i_data_buf_alloced:1; /* Set if custom data buffer allocated by dev handler */ unsigned int dh_data_buf_alloced:1; + /* + * Set length of each member of dif_sg was normalized to match + * tgtt->hw_dif_same_sg_layout_required requirements + */ + unsigned int dif_sg_normalized:1; + /* Set if the target driver called scst_set_expected() */ unsigned int expected_values_set:1; - /* Set if the SG buffer was modified by scst_adjust_sg() */ + /* Set if the SG buffer was modified by scst_adjust_sg*() */ unsigned int sg_buff_modified:1; + /* Set if the DIF SG buffer was modified by scst_adjust_sg*() */ + unsigned int dif_sg_buff_modified:1; + /* * Set if cmd buffer was vmallocated and copied from more * then one sg chunk @@ -2141,6 +2418,9 @@ struct scst_cmd { /* Set if any direction residual is possible */ unsigned int resid_possible:1; + /* Set if DIF data should be included in the residual considerations */ + unsigned int tgt_dif_data_expected:1; + /* Set if cmd is done */ unsigned int done:1; @@ -2234,7 +2514,7 @@ struct scst_cmd { /* Values supplied by the initiator in the transport layer header, if any */ scst_data_direction expected_data_direction; - int expected_transfer_len; + int expected_transfer_len_full; /* both data and DIF tags, if any */ int expected_out_transfer_len; /* for bidi writes */ int64_t lba; /* LBA of this cmd */ @@ -2250,11 +2530,15 @@ struct scst_cmd { void (*scst_cmd_done)(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context); - struct sgv_pool_obj *sgv; /* sgv object */ + struct sgv_pool_obj *sgv; /* data sgv object */ int bufflen; /* cmd buffer length */ int sg_cnt; /* SG segments count */ struct scatterlist *sg; /* cmd data buffer SG vector */ + struct sgv_pool_obj *dif_sgv; /* DIF sgv object */ + struct scatterlist *dif_sg; /* cmd DIF tags buffer SG vector */ + int dif_sg_cnt; /* DIF SG segments count */ + /* * Response data length in data buffer. Must not be set * directly, use scst_set_resp_data_len() for that. @@ -2290,6 +2574,7 @@ struct scst_cmd { struct sgv_pool_obj *out_sgv; /* WRITE sgv object */ struct scatterlist *out_sg; /* WRITE data buffer SG vector */ int out_sg_cnt; /* WRITE SG segments count */ + /* No out DIF tags buffer, because there's no BIDI command with them */ /* * Used if both target driver or SCST core for internal commands and @@ -2302,8 +2587,15 @@ struct scst_cmd { */ struct scatterlist *tgt_i_sg; int tgt_i_sg_cnt; + int tgt_i_dif_sg_cnt; + struct scatterlist *tgt_i_dif_sg; /* DIF tags */ + /* + * There's no tgt_i_dif_sg_cnt, because it's supposed to be strictly + * bound to tgt_i_sg_cnt. + */ struct scatterlist *tgt_out_sg; /* bidirectional */ int tgt_out_sg_cnt; /* bidirectional */ + /* No out DIF tags buffer, because there's no BIDI command with them */ /* * The status fields in case of errors must be set using @@ -2314,6 +2606,12 @@ struct scst_cmd { uint8_t host_status; /* set by low-level driver to indicate status */ uint8_t driver_status; /* set by mid-level */ + /* DIF actions on this cmd */ + enum scst_dif_actions cmd_dif_actions; +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS + uint32_t cmd_corrupt_dif_tag; +#endif + uint8_t *sense; /* pointer to sense buffer */ unsigned short sense_valid_len; /* length of valid sense data */ unsigned short sense_buflen; /* length of the sense buffer, if any */ @@ -2339,8 +2637,8 @@ struct scst_cmd { */ struct list_head mgmt_cmd_list; - /* Used to restore sg if it was modified by scst_adjust_sg() */ - struct scst_orig_sg_data orig_sg; + /* Used to restore sg if it was modified by scst_adjust_sg*() */ + struct scst_orig_sg_data orig_sg, orig_dif_sg; /* Per opcode stuff */ union { @@ -2519,6 +2817,9 @@ struct scst_device { */ unsigned int dev_unregistering:1; + /* Set if this device does not support DIF IP checking */ + unsigned int dev_dif_ip_not_supported:1; + /**************************************************************/ /************************************************************* @@ -2536,6 +2837,8 @@ struct scst_device { unsigned int tas:1; unsigned int swp:1; unsigned int d_sense:1; + unsigned int dpicz:1; + unsigned int ato:1; /** ** Saved and default versions of them, which supported. TST is not @@ -2564,6 +2867,9 @@ struct scst_device { unsigned int d_sense_saved:1; unsigned int d_sense_default:1; + unsigned int dpicz_saved:1; + unsigned int dpicz_default:1; + /* * Set if device implements own ordered commands management. If not set * and queue_alg is SCST_QUEUE_ALG_0_RESTRICTED_REORDER, expected_sn @@ -2573,6 +2879,22 @@ struct scst_device { /**************************************************************/ + /* + * SCST_DIF_CHECK_APP_TAG (to match usage in the xPROTECT parsing + * routines), if app tag is checked, or 0 otherwise. Might be used + * as bool. + */ + unsigned int dif_app_chk; + + /* + * SCST_DIF_CHECK_REF_TAG (to match usage in the xPROTECT parsing + * routines), if ref tag is checked, or 0 otherwise. Might be used + * as bool. + */ + unsigned int dif_ref_chk; + + /**************************************************************/ + /* * Device block size and block shift if fixed size blocks used. Supposed * to be read-only or serialized the same way as MODE pages changes. @@ -2614,6 +2936,34 @@ struct scst_device { /* List of commands with lock, if dedicated threads are used */ struct scst_cmd_threads dev_cmd_threads; + /************************************************************* + ** T10-PI fields. Read-only, hence no protection. + *************************************************************/ + + enum scst_dif_mode dev_dif_mode; + int dev_dif_type; /* SCSI DIF type */ + + /* + * Callback to process DIF tags by SCST as required by device + * formatting and *protect cmd's bits. Supposed to return 0 on + * success, i.e. when cmd processing should proceed normally, or + * negative error code otherwise, i.e. when cmd processing should + * be stopped and status send to its initiator. + */ + int (*dev_dif_fn)(struct scst_cmd *cmd); + + __be16 dev_dif_static_app_tag; /* fixed APP TAG for all blocks in dev */ + __be32 dev_dif_static_app_ref_tag; /* fixed APP TAG part from REF + * TAG for all blocks in dev. + * Valid only with dif type 3 */ + + /* Cache to optimize scst_parse_*protect() routines */ + enum scst_dif_actions dev_dif_rd_actions; + enum scst_dif_actions dev_dif_wr_actions; + enum scst_dif_actions dev_dif_rd_prot0_actions; + enum scst_dif_actions dev_dif_wr_prot0_actions; + enum scst_dif_actions dev_dif_vr_actions; + /* Set if reserved via the SPC-2 SCSI RESERVE command. */ struct scst_session *reserved_by; @@ -2766,6 +3116,9 @@ struct scst_tgt_dev { /* Set if tgt_dev uses clustered SGV pool */ unsigned int tgt_dev_clust_pool:1; + /* Taken from the target on initialization to save a cache miss */ + unsigned hw_dif_same_sg_layout_required:1; + /**************************************************************/ /* @@ -2777,6 +3130,15 @@ struct scst_tgt_dev { /* Used for storage of dev handler private stuff */ void *dh_priv; + /* Pointer to function to compute DIF guard tag */ + __be16 (*tgt_dev_dif_crc_fn)(const void *buffer, unsigned int len); + + /* + * Guard tags format, one of SCST_DIF_GUARD_FORMAT_* constants. + * Put here to save extra dereferences, this space isn't used anyway. + */ + int tgt_dev_dif_guard_format; + /* How many cmds alive on this dev in this session */ atomic_t tgt_dev_cmd_count ____cacheline_aligned_in_smp; @@ -2822,6 +3184,11 @@ struct scst_tgt_dev { /* Set if INQUIRY DATA HAS CHANGED UA is needed */ unsigned int inq_changed_ua_needed:1; + /* How many DIF failures detected on this tgt_dev on the corresponding stage */ + atomic_t tgt_dev_dif_app_failed_tgt, tgt_dev_dif_ref_failed_tgt, tgt_dev_dif_guard_failed_tgt; + atomic_t tgt_dev_dif_app_failed_scst, tgt_dev_dif_ref_failed_scst, tgt_dev_dif_guard_failed_scst; + atomic_t tgt_dev_dif_app_failed_dev, tgt_dev_dif_ref_failed_dev, tgt_dev_dif_guard_failed_dev; + /* * Stored Unit Attention sense and its length for possible * subsequent REQUEST SENSE. Both protected by tgt_dev_lock. @@ -2857,6 +3224,9 @@ struct scst_acg_dev { /* If set, the corresponding LU is read only */ unsigned int acg_dev_rd_only:1; + /* Guard tags format, one of SCST_DIF_GUARD_FORMAT_* constants */ + int acg_dev_dif_guard_format; + struct scst_acg *acg; /* parent acg */ /* List entry in dev->dev_acg_dev_list */ @@ -3349,6 +3719,99 @@ static inline void scst_tgt_set_tgt_priv(struct scst_tgt *tgt, void *val) tgt->tgt_priv = val; } +/* + * Get/Set functions for tgt's tgt_dif_supported + */ +static inline bool scst_tgt_get_dif_supported(struct scst_tgt *tgt) +{ + return tgt->tgt_dif_supported; +} + +static inline void scst_tgt_set_dif_supported(struct scst_tgt *tgt, bool val) +{ + tgt->tgt_dif_supported = !!val; +} + +/* + * Get/Set functions for tgt's tgt_hw_dif_type1_supported + */ +static inline bool scst_tgt_get_hw_dif_type1_supported(struct scst_tgt *tgt) +{ + return tgt->tgt_hw_dif_type1_supported; +} + +static inline void scst_tgt_set_hw_dif_type1_supported(struct scst_tgt *tgt, bool val) +{ + tgt->tgt_hw_dif_type1_supported = !!val; +} + +/* + * Get/Set functions for tgt's tgt_hw_dif_type2_supported + */ +static inline bool scst_tgt_get_hw_dif_type2_supported(struct scst_tgt *tgt) +{ + return tgt->tgt_hw_dif_type2_supported; +} + +static inline void scst_tgt_set_hw_dif_type2_supported(struct scst_tgt *tgt, bool val) +{ + tgt->tgt_hw_dif_type2_supported = !!val; +} + +/* + * Get/Set functions for tgt's tgt_hw_dif_type3_supported + */ +static inline bool scst_tgt_get_hw_dif_type3_supported(struct scst_tgt *tgt) +{ + return tgt->tgt_hw_dif_type3_supported; +} + +static inline void scst_tgt_set_hw_dif_type3_supported(struct scst_tgt *tgt, bool val) +{ + tgt->tgt_hw_dif_type3_supported = !!val; +} + +/* + * Get/Set functions for tgt's tgt_hw_dif_ip_supported + */ +static inline bool scst_tgt_get_hw_dif_ip_supported(struct scst_tgt *tgt) +{ + return tgt->tgt_hw_dif_ip_supported; +} + +static inline void scst_tgt_set_hw_dif_ip_supported(struct scst_tgt *tgt, bool val) +{ + tgt->tgt_hw_dif_ip_supported = !!val; +} + +/* + * Get/Set functions for tgt's tgt_hw_dif_same_sg_layout_required + */ +static inline bool scst_tgt_get_hw_dif_same_sg_layout_required(struct scst_tgt *tgt) +{ + return tgt->tgt_hw_dif_same_sg_layout_required; +} + +static inline void scst_tgt_set_hw_dif_same_sg_layout_required(struct scst_tgt *tgt, bool val) +{ + tgt->tgt_hw_dif_same_sg_layout_required = !!val; +} + +/* + * Get/Set functions for tgt's tgt_supported_dif_block_sizes + */ +static inline const int *scst_tgt_get_supported_dif_block_sizes( + struct scst_tgt *tgt) +{ + return tgt->tgt_supported_dif_block_sizes; +} + +static inline void scst_tgt_set_supported_dif_block_sizes(struct scst_tgt *tgt, + const int *const val) +{ + tgt->tgt_supported_dif_block_sizes = val; +} + void scst_update_hw_pending_start(struct scst_cmd *cmd); /* @@ -3370,6 +3833,115 @@ bool scst_impl_alua_configured(struct scst_device *dev); int scst_tg_get_group_info(void **buf, uint32_t *response_length, struct scst_device *dev, uint8_t data_format); +/* + * Get/set functions for dev's static DIF APP TAG + */ +static inline __be16 scst_dev_get_dif_static_app_tag(struct scst_device *dev) +{ + return dev->dev_dif_static_app_tag; +} + +static inline __be32 scst_dev_get_dif_static_app_ref_tag(struct scst_device *dev) +{ + return dev->dev_dif_static_app_ref_tag; +} + +static inline __be64 scst_dev_get_dif_static_app_tag_combined( + struct scst_device *dev) +{ + uint64_t a = (((uint64_t)be32_to_cpu(dev->dev_dif_static_app_ref_tag)) << 16) | + be16_to_cpu(dev->dev_dif_static_app_tag); + return cpu_to_be64(a); +} + +void scst_dev_set_dif_static_app_tag_combined(struct scst_device *dev, + __be64 app_tag); + +/* + * Get/set functions for dev's DIF APP TAG checking + */ +static inline bool scst_dev_get_dif_app_tag_check(struct scst_device *dev) +{ + return dev->dif_app_chk == SCST_DIF_CHECK_APP_TAG; +} + +int scst_set_dif_params(struct scst_device *dev, + enum scst_dif_mode dif_mode, int dif_type); + +/* + * Functions to account detected DIF errors on the corresponding stages + */ +static inline void scst_dif_acc_app_check_failed_tgt(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_app_failed_tgt); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_app_failed_tgt); +} + +static inline void scst_dif_acc_ref_check_failed_tgt(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_ref_failed_tgt); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_ref_failed_tgt); +} + +static inline void scst_dif_acc_guard_check_failed_tgt(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_guard_failed_tgt); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_guard_failed_tgt); +} + +static inline void scst_dif_acc_app_check_failed_scst(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_app_failed_scst); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_app_failed_scst); +} + +static inline void scst_dif_acc_ref_check_failed_scst(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_ref_failed_scst); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_ref_failed_scst); +} + +static inline void scst_dif_acc_guard_check_failed_scst(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_guard_failed_scst); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_guard_failed_scst); +} + +static inline void scst_dif_acc_app_check_failed_dev(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_app_failed_dev); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_app_failed_dev); +} + +static inline void scst_dif_acc_ref_check_failed_dev(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_ref_failed_dev); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_ref_failed_dev); +} + +static inline void scst_dif_acc_guard_check_failed_(struct scst_cmd *cmd) +{ + atomic_inc(&cmd->tgt->tgt_dif_guard_failed_dev); + atomic_inc(&cmd->tgt_dev->tgt_dev_dif_guard_failed_dev); +} + +/* + * Functions to process DIF tags by SCST as required by device + * formatting and *protect cmd's bits. + * + * Return 0 on success, i.e. when cmd processing should proceed normally, + * or negative error code otherwise, i.e. when cmd processing should be + * stopped and status send to its initiator. + */ +static inline int scst_dif_process_read(struct scst_cmd *cmd) +{ + return cmd->dev->dev_dif_fn(cmd); +} +static inline int scst_dif_process_write(struct scst_cmd *cmd) +{ + return cmd->dev->dev_dif_fn(cmd); +} + /** * Returns TRUE if cmd is being executed in atomic context. * @@ -3400,6 +3972,12 @@ static inline bool scst_cmd_atomic(struct scst_cmd *cmd) return res; } +/* Returns TRUE if cmd completed with SAM_STAT_GOOD */ +static inline bool scst_cmd_completed_good(struct scst_cmd *cmd) +{ + return cmd->completed && (cmd->status == SAM_STAT_GOOD); +} + /* * Returns TRUE if cmd has been preliminary completed, i.e. completed or * aborted. @@ -3503,6 +4081,28 @@ static inline int scst_cmd_get_sg_cnt(struct scst_cmd *cmd) return cmd->sg_cnt; } +/* + * Returns pointer to cmd's DIF tags SG data buffer. + * + * Usage of this function is not recommended, use scst_get_dif_buf() + * function instead. + */ +static inline struct scatterlist *scst_cmd_get_dif_sg(struct scst_cmd *cmd) +{ + return cmd->dif_sg; +} + +/* + * Returns pointer to cmd's DIF tags SG data buffer elements count. + * + * Usage of this function is not recommended, use scst_get_dif_buf() + * function instead. + */ +static inline int scst_cmd_get_dif_sg_cnt(struct scst_cmd *cmd) +{ + return cmd->dif_sg_cnt; +} + /* Returns cmd's LBA */ static inline int64_t scst_cmd_get_lba(struct scst_cmd *cmd) { @@ -3530,6 +4130,27 @@ static inline int64_t scst_cmd_get_data_len(struct scst_cmd *cmd) return cmd->data_len; } +/* Returns true, if cmd needs DIF buffer */ +static inline bool scst_cmd_needs_dif_buf(struct scst_cmd *cmd) +{ + return (scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE) || + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE); +} + +/* Returns length of DIF buffer of this cmd */ +static inline int __scst_cmd_get_bufflen_dif(struct scst_cmd *cmd) +{ + return (cmd->bufflen >> cmd->dev->block_shift) << SCST_DIF_TAG_SHIFT; +} + +static inline int scst_cmd_get_bufflen_dif(struct scst_cmd *cmd) +{ + if (scst_cmd_needs_dif_buf(cmd)) + return __scst_cmd_get_bufflen_dif(cmd); + else + return 0; +} + /* * Returns pointer to cmd's bidirectional in (WRITE) SG data buffer. * @@ -3557,7 +4178,7 @@ void scst_restore_sg_buff(struct scst_cmd *cmd); /* Restores modified sg buffer in the original state, if necessary */ static inline void scst_check_restore_sg_buff(struct scst_cmd *cmd) { - if (unlikely(cmd->sg_buff_modified)) + if (unlikely(cmd->sg_buff_modified || cmd->dif_sg_buff_modified)) scst_restore_sg_buff(cmd); } @@ -3603,6 +4224,35 @@ static inline void scst_cmd_set_tgt_sg(struct scst_cmd *cmd, cmd->tgt_i_data_buf_alloced = 1; } +/* + * Returns pointer to cmd's target's DIF tags SG data buffer. Since it's + * for target drivers, the "_i_" part is omitted. + */ +static inline struct scatterlist *scst_cmd_get_tgt_dif_sg(struct scst_cmd *cmd) +{ + return cmd->tgt_i_dif_sg; +} + +/* + * Returns cmd's target's DIF tags SG data buffer elements count. Since it's + * for target drivers, the "_i_" part is omitted. + */ +static inline int scst_cmd_get_tgt_dif_sg_cnt(struct scst_cmd *cmd) +{ + return cmd->tgt_i_dif_sg_cnt; +} + +/* + * Sets cmd's target's DIF tags SG data buffer. Since it's for target + * drivers, the "_i_" part is omitted. + */ +static inline void scst_cmd_set_tgt_dif_sg(struct scst_cmd *cmd, + struct scatterlist *dif_sg, int cnt) +{ + cmd->tgt_i_dif_sg = dif_sg; + cmd->tgt_i_dif_sg_cnt = cnt; +} + /* Returns pointer to cmd's target's OUT SG data buffer */ static inline struct scatterlist *scst_cmd_get_out_tgt_sg(struct scst_cmd *cmd) { @@ -3865,12 +4515,19 @@ static inline scst_data_direction scst_cmd_get_expected_data_direction( return cmd->expected_data_direction; } -static inline int scst_cmd_get_expected_transfer_len( +/* + * Returns full expected transfer length, i.e. including both data and + * DIF tags, if any. + */ +static inline int scst_cmd_get_expected_transfer_len_full( struct scst_cmd *cmd) { - return cmd->expected_transfer_len; + return cmd->expected_transfer_len_full; } +int scst_cmd_get_expected_transfer_len_data(struct scst_cmd *cmd); +int scst_cmd_get_expected_transfer_len_dif(struct scst_cmd *cmd); + static inline int scst_cmd_get_expected_out_transfer_len( struct scst_cmd *cmd) { @@ -3879,10 +4536,10 @@ static inline int scst_cmd_get_expected_out_transfer_len( static inline void scst_cmd_set_expected(struct scst_cmd *cmd, scst_data_direction expected_data_direction, - int expected_transfer_len) + int expected_transfer_len_full) { cmd->expected_data_direction = expected_data_direction; - cmd->expected_transfer_len = expected_transfer_len; + cmd->expected_transfer_len_full = expected_transfer_len_full; cmd->expected_values_set = 1; } @@ -3922,6 +4579,168 @@ static inline void scst_set_delivery_status(struct scst_cmd *cmd, cmd->delivery_status = delivery_status; } +/* + * Returns T10-PI actions, including checks, which target driver should do + * with this command on READ direction transfers (xmit_response()). Might + * be different from WRITE direction for BIDI commands. + */ +static inline enum scst_dif_actions scst_get_read_dif_tgt_actions( + struct scst_cmd *cmd) +{ + return scst_get_tgt_dif_actions(cmd->cmd_dif_actions); +} + +/* + * Returns T10-PI actions, including checks, which target driver should do + * with this command on WRITE direction transfers (rdy_to_xfer()). Might + * be different from READ direction for BIDI commands. + */ +static inline enum scst_dif_actions scst_get_write_dif_tgt_actions( + struct scst_cmd *cmd) +{ + return scst_get_tgt_dif_actions(cmd->cmd_dif_actions); +} + +/* + * Returns T10-PI actions, including checks, which dev handler should do + * with this command on READ direction transfers. Might be different + * from WRITE direction for BIDI commands. + */ +static inline enum scst_dif_actions scst_get_read_dif_dev_actions( + struct scst_cmd *cmd) +{ + return scst_get_dev_dif_actions(cmd->cmd_dif_actions); +} + +/* + * Returns T10-PI actions, including checks, which dev handler should do + * with this command on WRITE direction transfers. Might be different + * from READ direction for BIDI commands. + */ +static inline enum scst_dif_actions scst_get_write_dif_dev_actions( + struct scst_cmd *cmd) +{ + return scst_get_dev_dif_actions(cmd->cmd_dif_actions); +} + +/* + * Returns T10-PI protection type for this cmd's device. Supposed to be used + * by target drivers. + */ +static inline int scst_cmd_get_dif_prot_type(struct scst_cmd *cmd) +{ + return cmd->dev->dev_dif_type; +} + +/* + * Returns T10-PI application tag for this cmd's device's lba. + * + * If SCST_DIF_NO_CHECK_APP_TAG returned, the target driver should not + * check app tag for this lba (SCSI requirement). + * + * Parameter out_lba_end returns end LBA of the area, where this app tag + * is valid. Then, if necessary, the target driver supposed to call + * this function again for the next app tag area. This is intended to + * support Application Tag mode page. ToDo, not implemented yet. + */ +static inline __be16 scst_cmd_get_dif_app_tag(struct scst_cmd *cmd, + uint64_t lba_start/*, uint64_t *out_lba_end*/) +{ +#ifdef CONFIG_SCST_EXTRACHECKS + WARN_ON(!(scst_get_dif_checks(cmd->cmd_dif_actions) & SCST_DIF_CHECK_APP_TAG)); +#endif + return cmd->dev->dev_dif_static_app_tag; +} + +/* + * Returns T10-PI type 2 expected initial reference tag as LBA, i.e. converted + * into CPU endianness. Valid only with protection type 2. + */ +static inline uint32_t scst_cmd_get_dif_exp_ref_tag(struct scst_cmd *cmd) +{ +#ifdef CONFIG_SCST_EXTRACHECKS + BUG_ON(cmd->dev->dev_dif_type != 2); + WARN_ON(cmd->cdb_len > 32); +#endif + if (cmd->cdb_len == 32) + return get_unaligned_be32(&cmd->cdb[20]); + else + return cmd->lba & 0xFFFFFFFF; +} + +/* + * Returns T10-PI type 2 expected logical block application tag converted + * into CPU endianness. Valid only with protection type 2. + */ +static inline uint16_t scst_cmd_get_dif_exp_app_tag(struct scst_cmd *cmd) +{ +#ifdef CONFIG_SCST_EXTRACHECKS + BUG_ON(cmd->dev->dev_dif_type != 2); + WARN_ON(cmd->cdb_len > 32); +#endif + if (cmd->cdb_len == 32) + return get_unaligned_be16(&cmd->cdb[24]); + else { + /* cmd->dev must be alive at this point */ + return be16_to_cpu(cmd->dev->dev_dif_static_app_tag); + } +} + +/* + * Returns T10-PI type 2 logical block application tag mask converted + * into CPU endianness. Valid only with protection type 2. + */ +static inline uint16_t scst_cmd_get_dif_app_tag_mask(struct scst_cmd *cmd) +{ +#ifdef CONFIG_SCST_EXTRACHECKS + BUG_ON(cmd->dev->dev_dif_type != 2); + WARN_ON(cmd->cdb_len > 32); +#endif + if (cmd->cdb_len == 32) + return get_unaligned_be16(&cmd->cdb[26]); + else { + if (scst_get_dif_checks(cmd->cmd_dif_actions) & SCST_DIF_CHECK_APP_TAG) + return 0xFFFF; + else + return 0; + } +} + +/* + * Returns T10-PI type 3 application/reference tag. Valid only with type 3 + * protection type. Alternatively, scst_dev_get_dif_static_app_tag_combined() + * can be used. + * + * If SCST_DIF_NO_CHECK_ALL_REF_TAG returned, the target driver should not + * check the tag for this lba (SCSI requirement). + */ +static inline __be32 scst_cmd_get_dif_app_ref_tag(struct scst_cmd *cmd) +{ +#ifdef CONFIG_SCST_EXTRACHECKS + BUG_ON(cmd->dev->dev_dif_type != 3); + WARN_ON(!(scst_get_dif_checks(cmd->cmd_dif_actions) & SCST_DIF_CHECK_REF_TAG)); +#endif + return cmd->dev->dev_dif_static_app_ref_tag; +} + +/* + * Returns format of T10-PI GUARD TAGs as one of SCST_DIF_GUARD_FORMAT_* + * constants + */ +static inline int scst_cmd_get_dif_guard_format(struct scst_cmd *cmd) +{ + return cmd->tgt_dev->tgt_dev_dif_guard_format; +} + +/* + * Returns block size of this cmd's device. Supposed to be used + * by target drivers during T10-PI processing. + */ +static inline int scst_cmd_get_block_size(struct scst_cmd *cmd) +{ + return cmd->dev->block_size; +} + static inline unsigned int scst_get_active_cmd_count(struct scst_cmd *cmd) { if (likely(cmd->tgt_dev != NULL)) @@ -4069,6 +4888,10 @@ static inline void *sg_virt(struct scatterlist *sg) return page_address(sg_page(sg)) + sg->offset; } +static inline void sg_mark_end(struct scatterlist *sg) +{ +} + #ifndef __BACKPORT_LINUX_SCATTERLIST_H_TO_2_6_23__ static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) @@ -4195,6 +5018,45 @@ static inline void scst_put_buf(struct scst_cmd *cmd, void *buf) /* Nothing to do */ } +/* + * Functions for access to the commands DIF tags (SG) buffer. It doesn't + * return any error code, because this buffer is strictly connected to the + * data buffer, so for each block in that buffer, there must be tag here. + * + * Parameter psg on entrance specifies previous DIF tags SG in the access. + * Must point to NULL for the first access. On exit returns current DIF + * tags SG. + * + * The "put" function unmaps the buffer. + * + * !!! NOTE: this function does not check DIF SG cnt, hence must be used !!! + * !!! only inside code bound to the corresponding data SG cnt! !!! + */ +static inline uint8_t *scst_get_dif_buf(struct scst_cmd *cmd, + struct scatterlist **psg, int *length) +{ + uint8_t *buf; + struct scatterlist *sg; + + if (*psg == NULL) + sg = cmd->dif_sg; + else + sg = __sg_next_inline(*psg); + + buf = page_address(sg_page(sg)); + buf += sg->offset; + *length = sg->length; + + *psg = sg; + + return buf; +} + +static inline void scst_put_dif_buf(struct scst_cmd *cmd, void *buf) +{ + /* Nothing to do */ +} + static inline int scst_get_out_buf_first(struct scst_cmd *cmd, uint8_t **buf) { if (unlikely(cmd->out_sg == NULL)) { diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index 022f0ec77..b028e826c 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -1,8 +1,8 @@ /* * include/scst_const.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Contains common SCST constants. This file supposed to be included * from both kernel and user spaces. @@ -334,6 +334,9 @@ static inline int scst_sense_response_code(const uint8_t *sense) /* ABORTED_COMMAND is 0xb */ #define scst_sense_aborted_command ABORTED_COMMAND, 0x00, 0 +#define scst_logical_block_guard_check_failed ABORTED_COMMAND, 0x10, 1 +#define scst_logical_block_app_tag_check_failed ABORTED_COMMAND, 0x10, 2 +#define scst_logical_block_ref_tag_check_failed ABORTED_COMMAND, 0x10, 3 #define scst_sense_internal_failure ABORTED_COMMAND, 0x44, 0 /* retriable */ /* MISCOMPARE is 0xe */ @@ -427,6 +430,13 @@ static inline int scst_sense_response_code(const uint8_t *sense) #define UNMAP 0x42 #endif +/* Subcodes of VARIABLE_LENGTH_CMD (0x7F) */ +#define SUBCODE_READ_32 0x09 +#define SUBCODE_VERIFY_32 0x0a +#define SUBCODE_WRITE_32 0x0b +#define SUBCODE_WRITE_VERIFY_32 0x0c +#define SUBCODE_WRITE_SAME_32 0x0d + #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) /* * From . See also commit @@ -445,7 +455,6 @@ static inline int scst_sense_response_code(const uint8_t *sense) #define BLKDISCARD _IO(0x12,119) #endif - /************************************************************* ** SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft ** T10/1561-D Revision 4 Draft dated 7th November 2002. @@ -520,6 +529,44 @@ enum { #define SCST_QERR_2_RESERVED 2 #define SCST_QERR_3_ABORT_THIS_NEXUS_ONLY 3 +/************************************************************* + ** Values for the control mode page ATO field + *************************************************************/ + +/* I.e., app tags owned by storage */ +#define SCST_ATO_0_MODIFIED_BY_STORAGE 0 + +/* I.e., app tags owned by application */ +#define SCST_ATO_1_NOT_MODIFIED_BY_STORAGE 1 + +/************************************************************* + ** Values for the control mode page DPICZ field + *************************************************************/ +#define SCST_DPICZ_CHECK_ON_xPROT_0 0 +#define SCST_DPICZ_NO_CHECK_ON_xPROT_0 1 + +/************************************************************* + ** APP/REF TAG DIF constants + *************************************************************/ + +/* Skip all guards checks for this block */ +#define SCST_DIF_NO_CHECK_ALL_APP_TAG cpu_to_be16(0xFFFF) +#define SCST_DIF_NO_CHECK_ALL_REF_TAG cpu_to_be32(0xFFFFFFFF) + +/* Don't check app tag for this block */ +#define SCST_DIF_NO_CHECK_APP_TAG 0 + +/************************************************************* + ** Shift of size of DIF tag (8 bytes) + *************************************************************/ +#define SCST_DIF_TAG_SHIFT 3 + +/************************************************************* + ** Formats of DIF guard tags + *************************************************************/ +#define SCST_DIF_GUARD_FORMAT_CRC 0 +#define SCST_DIF_GUARD_FORMAT_IP 1 + /************************************************************* ** TransportID protocol identifiers *************************************************************/ diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index d5b343241..878d188b6 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -1,9 +1,9 @@ /* * include/scst_debug.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Contains macros for execution tracing and error reporting * diff --git a/scst/include/scst_sgv.h b/scst/include/scst_sgv.h index 8b5084492..36902931e 100644 --- a/scst/include/scst_sgv.h +++ b/scst/include/scst_sgv.h @@ -1,8 +1,8 @@ /* * include/scst_sgv.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Include file for SCST SGV cache. * diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index 55f1672d2..69e90bdea 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -1,8 +1,8 @@ /* * include/scst_user.h * - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Contains constants and data structures for scst_user module. * See http://scst.sourceforge.net/doc/scst_user_spec.txt or diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-3.19.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.19.patch new file mode 100644 index 000000000..39d30a55e --- /dev/null +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.19.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/Kconfig b/drivers/Kconfig +index c70d6e4..0a4ed1b 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -26,6 +26,8 @@ source "drivers/ide/Kconfig" + + source "drivers/scsi/Kconfig" + ++source "drivers/scst/Kconfig" ++ + source "drivers/ata/Kconfig" + + source "drivers/md/Kconfig" diff --git a/scst/kernel/in-tree/Makefile.dev_handlers-3.19 b/scst/kernel/in-tree/Makefile.dev_handlers-3.19 new file mode 100644 index 000000000..f933b36f7 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.dev_handlers-3.19 @@ -0,0 +1,14 @@ +ccflags-y += -Wno-unused-parameter + +obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \ + scst_vdisk.o scst_raid.o scst_processor.o scst_user.o + +obj-$(CONFIG_SCST_DISK) += scst_disk.o +obj-$(CONFIG_SCST_TAPE) += scst_tape.o +obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o +obj-$(CONFIG_SCST_MODISK) += scst_modisk.o +obj-$(CONFIG_SCST_CHANGER) += scst_changer.o +obj-$(CONFIG_SCST_RAID) += scst_raid.o +obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o +obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o +obj-$(CONFIG_SCST_USER) += scst_user.o diff --git a/scst/kernel/in-tree/Makefile.drivers.Linux-3.19.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-3.19.patch new file mode 100644 index 000000000..6bbacd12f --- /dev/null +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-3.19.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/Makefile b/drivers/Makefile +index 527a6da..db2c24f 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -137,6 +137,7 @@ obj-$(CONFIG_SSB) += ssb/ + obj-$(CONFIG_BCMA) += bcma/ + obj-$(CONFIG_VHOST_RING) += vhost/ + obj-$(CONFIG_VLYNQ) += vlynq/ ++obj-$(CONFIG_SCST) += scst/ + obj-$(CONFIG_STAGING) += staging/ + obj-y += platform/ + #common clk code diff --git a/scst/kernel/in-tree/Makefile.scst-3.19 b/scst/kernel/in-tree/Makefile.scst-3.19 new file mode 100644 index 000000000..53af5f388 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.scst-3.19 @@ -0,0 +1,13 @@ +ccflags-y += -Wno-unused-parameter + +scst-y += scst_main.o +scst-y += scst_pres.o +scst-y += scst_targ.o +scst-y += scst_lib.o +scst-y += scst_sysfs.o +scst-y += scst_mem.o +scst-y += scst_tg.o +scst-y += scst_debug.o + +obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \ + srpt/ scst_local/ diff --git a/scst/src/Makefile b/scst/src/Makefile index 4d7088d57..28107ff99 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -1,9 +1,9 @@ # # SCSI target mid-level makefile # -# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -121,6 +121,7 @@ install: all install -m 644 $(MODULE_SYMVERS) $(INSTALL_DIR_H) -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) mkdir -p $(DESTDIR)/var/lib/scst/pr + mkdir -p $(DESTDIR)/var/lib/scst/dif_tags mkdir -p $(DESTDIR)/var/lib/scst/vdev_mode_pages @echo "****************************************************************" @echo "*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*" diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index f3f8b960d..d5187a677 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -1,9 +1,9 @@ # # SCSI target mid-level dev handler's makefile # -# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/scst/src/dev_handlers/scst_cdrom.c b/scst/src/dev_handlers/scst_cdrom.c index c1ffb454b..1e5826d77 100644 --- a/scst/src/dev_handlers/scst_cdrom.c +++ b/scst/src/dev_handlers/scst_cdrom.c @@ -1,9 +1,9 @@ /* * scst_cdrom.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI CDROM (type 5) dev handler * @@ -177,15 +177,20 @@ out: static void cdrom_set_block_shift(struct scst_cmd *cmd, int block_shift) { struct scst_device *dev = cmd->dev; + int new_block_shift; + /* * No need for locks here, since *_detach() can not be * called, when there are existing commands. */ - if (block_shift != 0) - dev->block_shift = block_shift; - else - dev->block_shift = CDROM_DEF_BLOCK_SHIFT; - dev->block_size = 1 << dev->block_shift; + new_block_shift = block_shift ? : CDROM_DEF_BLOCK_SHIFT; + if (dev->block_shift != new_block_shift) { + PRINT_INFO("%s: Changed block shift from %d into %d / %d", + dev->virt_name, dev->block_shift, block_shift, + new_block_shift); + dev->block_shift = new_block_shift; + dev->block_size = 1 << dev->block_shift; + } return; } diff --git a/scst/src/dev_handlers/scst_changer.c b/scst/src/dev_handlers/scst_changer.c index 23eb11ce7..46e981972 100644 --- a/scst/src/dev_handlers/scst_changer.c +++ b/scst/src/dev_handlers/scst_changer.c @@ -1,9 +1,9 @@ /* * scst_changer.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI medium changer (type 8) dev handler * diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index ad58fc438..9ef9e8682 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -1,9 +1,9 @@ /* * scst_disk.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI disk (type 0) dev handler * & @@ -269,15 +269,20 @@ out: static void disk_set_block_shift(struct scst_cmd *cmd, int block_shift) { struct scst_device *dev = cmd->dev; + int new_block_shift; + /* * No need for locks here, since *_detach() can not be * called, when there are existing commands. */ - if (block_shift != 0) - dev->block_shift = block_shift; - else - dev->block_shift = DISK_DEF_BLOCK_SHIFT; - dev->block_size = 1 << dev->block_shift; + new_block_shift = block_shift ? : DISK_DEF_BLOCK_SHIFT; + if (dev->block_shift != new_block_shift) { + PRINT_INFO("%s: Changed block shift from %d into %d / %d", + dev->virt_name, dev->block_shift, block_shift, + new_block_shift); + dev->block_shift = new_block_shift; + dev->block_size = 1 << dev->block_shift; + } return; } @@ -355,11 +360,11 @@ static void disk_cmd_done(void *data, char *sense, int result, int resid) TRACE_DBG("work %p, cmd %p, left %d, result %d, sense %p, resid %d", work, work->cmd, work->left, result, sense, resid); + work->result = result; + if (result == SAM_STAT_GOOD) goto out_complete; - work->result = result; - disk_restore_sg(work); scst_pass_through_cmd_done(work->cmd, sense, result, resid + work->left); @@ -381,7 +386,7 @@ static int disk_exec(struct scst_cmd *cmd) struct scatterlist *sg, *start_sg; int cur_sg_cnt; int sg_tablesize = cmd->dev->scsi_dev->host->sg_tablesize; - int max_sectors; + unsigned int max_sectors; int num, j, block_shift = dev->block_shift; TRACE_ENTRY(); @@ -392,15 +397,10 @@ static int disk_exec(struct scst_cmd *cmd) */ max_sectors = queue_max_hw_sectors(dev->scsi_dev->request_queue); - if (unlikely(((max_sectors << block_shift) & ~PAGE_MASK) != 0)) { - int mlen = max_sectors << block_shift; - int pg = ((mlen >> PAGE_SHIFT) + ((mlen & ~PAGE_MASK) != 0)) - 1; - int adj_len = pg << PAGE_SHIFT; - max_sectors = adj_len >> block_shift; - if (max_sectors == 0) { - PRINT_ERROR("Too low max sectors %d", max_sectors); - goto out_error; - } + if (unlikely(max_sectors < (PAGE_SIZE >> block_shift))) { + PRINT_ERROR("Too low max sectors: %u << %u < %lu", max_sectors, + block_shift, PAGE_SIZE); + goto out_error; } if (unlikely((cmd->bufflen >> block_shift) > max_sectors)) { @@ -497,8 +497,7 @@ split: rc = scst_scsi_exec_async(cmd, &work, disk_cmd_done); if (unlikely(rc != 0)) { - PRINT_ERROR("scst_scsi_exec_async() failed: %d", - rc); + PRINT_ERROR("scst_scsi_exec_async() failed: %d", rc); goto out_err_restore; } diff --git a/scst/src/dev_handlers/scst_modisk.c b/scst/src/dev_handlers/scst_modisk.c index eb9e50d1d..b3461b1a9 100644 --- a/scst/src/dev_handlers/scst_modisk.c +++ b/scst/src/dev_handlers/scst_modisk.c @@ -1,9 +1,9 @@ /* * scst_modisk.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI MO disk (type 7) dev handler * & @@ -276,15 +276,20 @@ out: static void modisk_set_block_shift(struct scst_cmd *cmd, int block_shift) { struct scst_device *dev = cmd->dev; + int new_block_shift; + /* * No need for locks here, since *_detach() can not be * called, when there are existing commands. */ - if (block_shift != 0) - dev->block_shift = block_shift; - else - dev->block_shift = MODISK_DEF_BLOCK_SHIFT; - dev->block_size = 1 << dev->block_shift; + new_block_shift = block_shift ? : MODISK_DEF_BLOCK_SHIFT; + if (dev->block_shift != new_block_shift) { + PRINT_INFO("%s: Changed block shift from %d into %d / %d", + dev->virt_name, dev->block_shift, block_shift, + new_block_shift); + dev->block_shift = new_block_shift; + dev->block_size = 1 << dev->block_shift; + } return; } diff --git a/scst/src/dev_handlers/scst_processor.c b/scst/src/dev_handlers/scst_processor.c index cad04b576..82f584c27 100644 --- a/scst/src/dev_handlers/scst_processor.c +++ b/scst/src/dev_handlers/scst_processor.c @@ -1,9 +1,9 @@ /* * scst_processor.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI medium processor (type 3) dev handler * diff --git a/scst/src/dev_handlers/scst_raid.c b/scst/src/dev_handlers/scst_raid.c index 7f95d2b5c..e4cf7e3d4 100644 --- a/scst/src/dev_handlers/scst_raid.c +++ b/scst/src/dev_handlers/scst_raid.c @@ -1,9 +1,9 @@ /* * scst_raid.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI raid(controller) (type 0xC) dev handler * diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index 4aeba2417..4daa84d84 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -1,9 +1,9 @@ /* * scst_tape.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI tape (type 1) dev handler * & diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 5dabae13d..2d45bb4f1 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -1,8 +1,8 @@ /* * scst_user.c * - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * SCSI virtual user space device handler * @@ -47,8 +47,6 @@ #define DEV_USER_ATTACH_TIMEOUT (5*HZ) struct scst_user_dev { - struct rw_semaphore dev_rwsem; - /* * Must be kept here, because it's needed on the cleanup time, * when corresponding scst_dev is already dead. @@ -58,7 +56,10 @@ struct scst_user_dev { /* Protected by udev_cmd_threads.cmd_list_lock */ struct list_head ready_cmd_list; - /* Protected by dev_rwsem or don't need any protection */ + /* + * Don't need any protection or assignment in SCST_USER_SET_OPTIONS + * supposed to be serialized by the caller + */ unsigned int blocking:1; unsigned int cleanup_done:1; unsigned int tst:3; @@ -218,8 +219,6 @@ static struct kmem_cache *user_dev_cachep; static struct kmem_cache *user_cmd_cachep; static struct kmem_cache *user_get_cmd_cachep; -static DEFINE_MUTEX(dev_priv_mutex); - static const struct file_operations dev_user_fops = { .poll = dev_user_poll, .unlocked_ioctl = dev_user_ioctl, @@ -814,7 +813,7 @@ static int dev_user_parse(struct scst_cmd *cmd) ucmd->user_cmd.parse_cmd.expected_data_direction = cmd->expected_data_direction; ucmd->user_cmd.parse_cmd.expected_transfer_len = - cmd->expected_transfer_len; + cmd->expected_transfer_len_full; ucmd->user_cmd.parse_cmd.expected_out_transfer_len = cmd->expected_out_transfer_len; ucmd->user_cmd.parse_cmd.sn = cmd->tgt_sn; @@ -1020,6 +1019,8 @@ out_reply: static void dev_user_set_block_shift(struct scst_cmd *cmd, int block_shift) { struct scst_device *dev = cmd->dev; + struct scst_user_dev *udev = cmd->dev->dh_priv; + int new_block_shift; TRACE_ENTRY(); @@ -1027,14 +1028,15 @@ static void dev_user_set_block_shift(struct scst_cmd *cmd, int block_shift) * No need for locks here, since *_detach() can not be * called, when there are existing commands. */ - TRACE_DBG("dev %p, new block shift %d", dev, block_shift); - if (block_shift != 0) - dev->block_shift = block_shift; - else { - struct scst_user_dev *udev = cmd->dev->dh_priv; - dev->block_shift = scst_calc_block_shift(udev->def_block_size); + new_block_shift = block_shift ? : + scst_calc_block_shift(udev->def_block_size); + if (dev->block_shift != new_block_shift) { + PRINT_INFO("%s: Changed block shift from %d into %d / %d", + dev->virt_name, dev->block_shift, block_shift, + new_block_shift); + dev->block_shift = new_block_shift; + dev->block_size = 1 << dev->block_shift; } - dev->block_size = 1 << dev->block_shift; TRACE_EXIT(); return; @@ -1704,21 +1706,16 @@ static int dev_user_reply_cmd(struct file *file, void __user *arg) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (unlikely(res != 0)) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); rc = copy_from_user(&reply, arg, sizeof(reply)); if (unlikely(rc != 0)) { PRINT_ERROR("Failed to copy %d user's bytes", rc); res = -EFAULT; - goto out_up; + goto out; } TRACE_DBG("Reply for dev %s", dev->name); @@ -1727,10 +1724,7 @@ static int dev_user_reply_cmd(struct file *file, void __user *arg) res = dev_user_process_reply(dev, &reply); if (unlikely(res < 0)) - goto out_up; - -out_up: - up_read(&dev->dev_rwsem); + goto out; out: TRACE_EXIT_RES(res); @@ -1747,21 +1741,16 @@ static int dev_user_get_ext_cdb(struct file *file, void __user *arg) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (unlikely(res != 0)) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); rc = copy_from_user(&get, arg, sizeof(get)); if (unlikely(rc != 0)) { PRINT_ERROR("Failed to copy %d user's bytes", rc); res = -EFAULT; - goto out_up; + goto out; } TRACE_MGMT_DBG("Get ext cdb for dev %s", dev->name); @@ -1823,16 +1812,13 @@ out_cmd_put: out_put: ucmd_put(ucmd); -out_up: - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_RES(res); return res; out_unlock: spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock); - goto out_up; + goto out; } static int dev_user_process_scst_commands(struct scst_user_dev *dev) @@ -1976,15 +1962,10 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (unlikely(res != 0)) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); /* get_user() can't be used with 64-bit values on x86_32 */ rc = copy_from_user(&ureply, (uint64_t __user *) @@ -1993,7 +1974,7 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg) if (unlikely(rc != 0)) { PRINT_ERROR("Failed to copy %d user's bytes", rc); res = -EFAULT; - goto out_up; + goto out; } TRACE_DBG("ureply %lld (dev %s)", (unsigned long long int)ureply, @@ -2002,7 +1983,7 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg) cmd = kmem_cache_alloc(user_get_cmd_cachep, GFP_KERNEL); if (unlikely(cmd == NULL)) { res = -ENOMEM; - goto out_up; + goto out; } if (ureply != 0) { @@ -2066,16 +2047,13 @@ again: } else spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock); -out_up: - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_RES(res); return res; out_free: kmem_cache_free(user_get_cmd_cachep, cmd); - goto out_up; + goto out; } static long dev_user_ioctl(struct file *file, unsigned int cmd, @@ -2184,15 +2162,10 @@ static unsigned int dev_user_poll(struct file *file, poll_table *wait) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (unlikely(res != 0)) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock); @@ -2219,8 +2192,6 @@ static unsigned int dev_user_poll(struct file *file, poll_table *wait) out_unlock: spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock); - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_HRES(res); return res; @@ -2974,7 +2945,6 @@ static int dev_user_register_dev(struct file *file, goto out_put; } - init_rwsem(&dev->dev_rwsem); INIT_LIST_HEAD(&dev->ready_cmd_list); if (file->f_flags & O_NONBLOCK) { TRACE_DBG("%s", "Non-blocking operations"); @@ -3083,15 +3053,19 @@ static int dev_user_register_dev(struct file *file, goto out_unreg_handler; } - mutex_lock(&dev_priv_mutex); + spin_lock(&dev_list_lock); if (file->private_data != NULL) { - mutex_unlock(&dev_priv_mutex); + spin_unlock(&dev_list_lock); PRINT_ERROR("%s", "Device already registered"); res = -EINVAL; goto out_unreg_drv; } + /* + * Assumption here is that the private_data reading is atomic, + * hence could be lockless and without ACCESS_ONCE(). + */ file->private_data = dev; - mutex_unlock(&dev_priv_mutex); + spin_unlock(&dev_list_lock); out: TRACE_EXIT_RES(res); @@ -3126,57 +3100,9 @@ out_put: static int dev_user_unregister_dev(struct file *file) { - int res; - struct scst_user_dev *dev; - - TRACE_ENTRY(); - - mutex_lock(&dev_priv_mutex); - dev = file->private_data; - res = dev_user_check_reg(dev); - if (res != 0) { - mutex_unlock(&dev_priv_mutex); - goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); - - res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER); - if (res != 0) - goto out_up; - - up_read(&dev->dev_rwsem); - - mutex_lock(&dev_priv_mutex); - dev = file->private_data; - if (dev == NULL) { - mutex_unlock(&dev_priv_mutex); - goto out_resume; - } - - dev->blocking = 0; - wake_up_all(&dev->udev_cmd_threads.cmd_list_waitQ); - - down_write(&dev->dev_rwsem); - file->private_data = NULL; - mutex_unlock(&dev_priv_mutex); - - dev_user_exit_dev(dev); - - up_write(&dev->dev_rwsem); /* to make lockdep happy */ - - kmem_cache_free(user_dev_cachep, dev); - -out_resume: - scst_resume_activity(); - -out: - TRACE_EXIT_RES(res); - return res; - -out_up: - up_read(&dev->dev_rwsem); - goto out; + PRINT_WARNING("SCST_USER_UNREGISTER_DEVICE is obsolete and NOOP. " + "Closing fd should be used instead."); + return 0; } static int dev_user_flush_cache(struct file *file) @@ -3186,28 +3112,20 @@ static int dev_user_flush_cache(struct file *file) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (res != 0) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER); if (res != 0) - goto out_up; + goto out; sgv_pool_flush(dev->pool); sgv_pool_flush(dev->pool_clust); scst_resume_activity(); -out_up: - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_RES(res); return res; @@ -3220,20 +3138,13 @@ static int dev_user_capacity_changed(struct file *file) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (res != 0) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); scst_capacity_data_changed(dev->sdev); - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_RES(res); return res; @@ -3253,21 +3164,16 @@ static int dev_user_prealloc_buffer(struct file *file, void __user *arg) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (unlikely(res != 0)) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); rc = copy_from_user(&pre.in, arg, sizeof(pre.in)); if (unlikely(rc != 0)) { PRINT_ERROR("Failed to copy %d user's bytes", rc); res = -EFAULT; - goto out_up; + goto out; } TRACE_MEM("Prealloc buffer with size %dKB for dev %s", @@ -3280,7 +3186,7 @@ static int dev_user_prealloc_buffer(struct file *file, void __user *arg) ucmd = dev_user_alloc_ucmd(dev, GFP_KERNEL); if (ucmd == NULL) { res = -ENOMEM; - goto out_up; + goto out; } ucmd->buff_cached = 1; @@ -3333,9 +3239,6 @@ static int dev_user_prealloc_buffer(struct file *file, void __user *arg) out_put: ucmd_put(ucmd); -out_up: - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_RES(res); return res; @@ -3430,27 +3333,19 @@ static int dev_user_set_opt(struct file *file, const struct scst_user_opt *opt) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (res != 0) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER); if (res != 0) - goto out_up; + goto out; res = __dev_user_set_opt(dev, opt); scst_resume_activity(); -out_up: - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_RES(res); return res; @@ -3464,15 +3359,10 @@ static int dev_user_get_opt(struct file *file, void __user *arg) TRACE_ENTRY(); - mutex_lock(&dev_priv_mutex); dev = file->private_data; res = dev_user_check_reg(dev); - if (res != 0) { - mutex_unlock(&dev_priv_mutex); + if (unlikely(res != 0)) goto out; - } - down_read(&dev->dev_rwsem); - mutex_unlock(&dev_priv_mutex); opt.parse_type = dev->parse_type; opt.on_free_cmd_type = dev->on_free_cmd_type; @@ -3498,12 +3388,9 @@ static int dev_user_get_opt(struct file *file, void __user *arg) if (unlikely(rc != 0)) { PRINT_ERROR("Failed to copy to user %d bytes", rc); res = -EFAULT; - goto out_up; + goto out; } -out_up: - up_read(&dev->dev_rwsem); - out: TRACE_EXIT_RES(res); return res; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index af66c288a..3436167af 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -1,12 +1,12 @@ /* * scst_vdisk.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2007 Ming Zhang * Copyright (C) 2007 Ross Walker - * Copyright (C) 2007 - 2014 Fusion-io, Inc. - * Copyright (C) 2008 - 2014 Bart Van Assche + * Copyright (C) 2007 - 2015 SanDisk Corporation + * Copyright (C) 2008 - 2015 Bart Van Assche * * SCSI disk (type 0) and CDROM (type 5) dev handler using files * on file systems or block devices (VDISK) @@ -134,6 +134,10 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define DEF_TAS 0 #define DEF_DSENSE SCST_D_SENSE_0_FIXED_SENSE +#define DEF_DPICZ SCST_DPICZ_CHECK_ON_xPROT_0 + +#define DEF_DIF_FILENAME_TMPL (SCST_VAR_DIR "/dif_tags/%s.dif") + #ifdef CONFIG_SCST_PROC #define VDISK_PROC_HELP "help" #endif @@ -162,6 +166,7 @@ struct scst_vdisk_dev { unsigned int prevent_allow_medium_removal:1; unsigned int nullio:1; unsigned int blockio:1; + unsigned int blk_integrity:1; unsigned int cdrom_empty:1; unsigned int dummy:1; unsigned int read_zero:1; @@ -176,6 +181,7 @@ struct scst_vdisk_dev { unsigned int discard_zeroes_data:1; struct file *fd; + struct file *dif_fd; struct block_device *bdev; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) struct bio_set *vdisk_bioset; @@ -227,8 +233,13 @@ struct scst_vdisk_dev { int tgt_dev_cnt; - /* Only to pass it to attach() callback. Don't use it anywhere else! */ + char *dif_filename; + + /* Only to pass it to attach() callback. Don't use them anywhere else! */ int blk_shift; + enum scst_dif_mode dif_mode; + int dif_type; + __be64 dif_static_app_tag_combined; }; struct vdisk_cmd_params { @@ -267,6 +278,12 @@ static int num_threads = DEF_NUM_THREADS; module_param_named(num_threads, num_threads, int, S_IRUGO); MODULE_PARM_DESC(num_threads, "vdisk threads count"); +/* + * Used to serialize sense setting between blockio data and DIF tags + * unsuccessful readings/writings + */ +static spinlock_t vdev_err_lock; + static int vdisk_attach(struct scst_device *dev); static void vdisk_detach(struct scst_device *dev); static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev); @@ -291,6 +308,9 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p); static enum compl_status_e nullio_exec_write(struct vdisk_cmd_params *p); static enum compl_status_e blockio_exec_write(struct vdisk_cmd_params *p); static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p); +static enum compl_status_e nullio_exec_var_len_cmd(struct vdisk_cmd_params *p); +static enum compl_status_e blockio_exec_var_len_cmd(struct vdisk_cmd_params *p); +static enum compl_status_e fileio_exec_var_len_cmd(struct vdisk_cmd_params *p); static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua); static int vdisk_blockio_flush(struct block_device *bdev, gfp_t gfp_mask, bool report_error, struct scst_cmd *cmd, bool async); @@ -421,6 +441,8 @@ static ssize_t vdev_sysfs_inq_vend_specific_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdev_zero_copy_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdev_dif_filename_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); @@ -495,6 +517,8 @@ static struct kobj_attribute vdev_inq_vend_specific_attr = vdev_sysfs_inq_vend_specific_store); static struct kobj_attribute vdev_zero_copy_attr = __ATTR(zero_copy, S_IRUGO, vdev_zero_copy_show, NULL); +static struct kobj_attribute vdev_dif_filename_attr = + __ATTR(dif_filename, S_IRUGO, vdev_dif_filename_show, NULL); static struct kobj_attribute vcdrom_filename_attr = __ATTR(filename, S_IRUGO|S_IWUSR, vdev_sysfs_filename_show, @@ -657,7 +681,11 @@ static struct scst_dev_type vdisk_file_devtype = { "thin_provisioned, " "tst, " "write_through, " - "zero_copy", + "zero_copy, " + "dif_mode, " + "dif_type, " + "dif_static_app_tag, " + "dif_filename", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -695,6 +723,10 @@ static struct scst_dev_type vdisk_blk_devtype = { .dev_attrs = vdisk_blockio_attrs, .add_device_parameters = "blocksize, " + "dif_mode, " + "dif_type, " + "dif_static_app_tag, " + "dif_filename, " "filename, " "nv_cache, " "read_only, " @@ -739,6 +771,9 @@ static struct scst_dev_type vdisk_null_devtype = { .add_device_parameters = "blocksize, " "dummy, " + "dif_mode, " + "dif_type, " + "dif_static_app_tag, ", "read_only, " "removable, " "rotational, " @@ -822,14 +857,14 @@ static const char *vdev_get_filename(const struct scst_vdisk_dev *virt_dev) /* Returns fd, use IS_ERR(fd) to get error status */ static struct file *vdev_open_fd(const struct scst_vdisk_dev *virt_dev, - bool read_only) + const char *name, bool read_only) { int open_flags = 0; struct file *fd; TRACE_ENTRY(); - sBUG_ON(!virt_dev->filename); + sBUG_ON(!name); if (read_only) open_flags |= O_RDONLY; @@ -839,9 +874,11 @@ static struct file *vdev_open_fd(const struct scst_vdisk_dev *virt_dev, open_flags |= O_DIRECT; if (virt_dev->wt_flag && !virt_dev->nv_cache) open_flags |= O_DSYNC; - TRACE_DBG("Opening file %s, flags 0x%x", - virt_dev->filename, open_flags); - fd = filp_open(virt_dev->filename, O_LARGEFILE | open_flags, 0600); + + TRACE_DBG("Opening file %s, flags 0x%x", name, open_flags); + fd = filp_open(name, O_LARGEFILE | open_flags, 0600); + if (IS_ERR(fd)) + PRINT_ERROR("filp_open(%s) failed: %d", name, (int)PTR_ERR(fd)); TRACE_EXIT(); return fd; @@ -864,7 +901,7 @@ static void vdisk_blockio_check_flush_support(struct scst_vdisk_dev *virt_dev) goto out; } - inode = fd->f_dentry->d_inode; + inode = file_inode(fd); if (!S_ISBLK(inode->i_mode)) { PRINT_ERROR("%s is NOT a block device", virt_dev->filename); @@ -907,7 +944,8 @@ static void vdisk_check_tp_support(struct scst_vdisk_dev *virt_dev) fd_open = true; if (virt_dev->blockio) { - struct inode *inode = fd->f_dentry->d_inode; + struct inode *inode = file_inode(fd); + if (!S_ISBLK(inode->i_mode)) { PRINT_ERROR("%s is NOT a block device", virt_dev->filename); @@ -945,8 +983,9 @@ check: int block_shift = virt_dev->dev->block_shift; if (virt_dev->blockio) { struct request_queue *q; + sBUG_ON(!fd_open); - q = bdev_get_queue(fd->f_dentry->d_inode->i_bdev); + q = bdev_get_queue(file_inode(fd)->i_bdev); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || \ (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) virt_dev->unmap_opt_gran = q->limits.discard_granularity >> block_shift; @@ -1005,7 +1044,7 @@ static int vdisk_get_file_size(const char *filename, bool blockio, goto out; } - inode = fd->f_dentry->d_inode; + inode = file_inode(fd); if (blockio && !S_ISBLK(inode->i_mode)) { PRINT_ERROR("File %s is NOT a block device", filename); @@ -1307,6 +1346,112 @@ out: return res; } +#if defined(CONFIG_BLK_DEV_INTEGRITY) +static int vdisk_init_block_integrity(struct scst_vdisk_dev *virt_dev) +{ + int res; + struct scst_device *dev = virt_dev->dev; + struct inode *inode; + struct file *fd; + struct blk_integrity *bi; + + TRACE_ENTRY(); + + fd = vdev_open_fd(virt_dev, virt_dev->filename, virt_dev->rd_only); + if (IS_ERR(fd)) { + res = -EINVAL; + goto out; + } + + inode = file_inode(fd); + + if (!S_ISBLK(inode->i_mode)) { + PRINT_ERROR("%s is NOT a block device!", virt_dev->filename); + res = -EINVAL; + goto out_close; + } + + bi = bdev_get_integrity(inode->i_bdev); + if (bi == NULL) { + TRACE_DBG("Block integrity not supported"); + goto out_no_bi; + } + + TRACE_DBG("BI name %s", bi->name); + + if (!strcmp(bi->name, "T10-DIF-TYPE1-CRC")) { + dev->dev_dif_ip_not_supported = 1; + if (virt_dev->dif_type != 1) { + PRINT_ERROR("Integrity type mismatch, %d expected, " + "but block device has 1 (dev %s)", + virt_dev->dif_type, dev->virt_name); + res = -EINVAL; + goto out_close; + } + } else if (!strcmp(bi->name, "T10-DIF-TYPE1-IP")) { + if (virt_dev->dif_type != 1) { + PRINT_ERROR("Integrity type mismatch, %d expected, " + "but block device has 1 (dev %s)", + virt_dev->dif_type, dev->virt_name); + res = -EINVAL; + goto out_close; + } + } else if (!strcmp(bi->name, "T10-DIF-TYPE3-CRC")) { + dev->dev_dif_ip_not_supported = 1; + if (virt_dev->dif_type != 3) { + PRINT_ERROR("Integrity type mismatch, %d expected, " + "but block device has 1 (dev %s)", + virt_dev->dif_type, dev->virt_name); + res = -EINVAL; + goto out_close; + } + } else if (!strcmp(bi->name, "T10-DIF-TYPE3-IP")) { + if (virt_dev->dif_type != 3) { + PRINT_ERROR("Integrity type mismatch, %d expected, " + "but block device has 3 (dev %s)", + virt_dev->dif_type, dev->virt_name); + res = -EINVAL; + goto out_close; + } + } else { + PRINT_ERROR("Unable to understand integrity name %s" + "(dev %s)", bi->name, dev->virt_name); + res = -EINVAL; + goto out_close; + } + + virt_dev->blk_integrity = 1; + + if ((virt_dev->dif_mode & SCST_DIF_MODE_DEV_CHECK) && + !(virt_dev->dif_mode & SCST_DIF_MODE_DEV_STORE)) { + PRINT_ERROR("Blockio dev_check is not possible without " + "dev_store (dev %s)", dev->virt_name); + res = -EINVAL; + goto out_close; + } + + if (!(virt_dev->dif_mode & SCST_DIF_MODE_DEV_CHECK)) + PRINT_WARNING("Blk integrity implies dev_check (dev %s)", + dev->virt_name); + +out_no_bi: + res = 0; + +out_close: + filp_close(fd, NULL); + +out: + TRACE_EXIT_RES(res); + return res; +} +#else /* defined(CONFIG_BLK_DEV_INTEGRITY) */ +static int vdisk_init_block_integrity(struct scst_vdisk_dev *virt_dev) +{ + PRINT_ERROR("Kernel does not support block device integrity"); + return -EINVAL; +} +#endif /* defined(CONFIG_BLK_DEV_INTEGRITY) */ + /* * Reexamine size, flush support and thin provisioning support for * vdisk_fileio, vdisk_blockio and vdisk_cdrom devices. Do not modify the size @@ -1367,6 +1512,76 @@ static int vdisk_attach(struct scst_device *dev) dev->block_shift = virt_dev->blk_shift; dev->block_size = 1 << dev->block_shift; + if ((virt_dev->dif_type == 0) && + ((virt_dev->dif_mode != SCST_DIF_MODE_NONE) || + (virt_dev->dif_filename != NULL))) { + PRINT_ERROR("Device %s cannot have DIF TYPE 0 if DIF MODE is " + "not NONE or DIF FILENAME is not NULL", virt_dev->name); + res = -EINVAL; + goto out; + } + + if (virt_dev->blockio) { + if (!(virt_dev->dif_mode & SCST_DIF_MODE_DEV)) + goto next; + + res = vdisk_init_block_integrity(virt_dev); + if (res != 0) + goto out; + } else if (virt_dev->dif_mode & SCST_DIF_MODE_DEV_CHECK) { + PRINT_ERROR("dev_check supported only for BLOCKIO devices " + "(dev %s)!", dev->virt_name); + res = -EINVAL; + goto out; + } + +next: + if ((virt_dev->dif_mode & SCST_DIF_MODE_DEV_STORE) && + (virt_dev->dif_filename == NULL) && !virt_dev->blk_integrity) { + virt_dev->dif_filename = kasprintf(GFP_KERNEL, + DEF_DIF_FILENAME_TMPL, dev->virt_name); + if (virt_dev->dif_filename == NULL) { + PRINT_ERROR("Allocation of default dif_filename " + "failed (dev %s)", dev->virt_name); + res = -ENOMEM; + goto out; + } + } + + if (virt_dev->dif_filename != NULL) { + /* Check if it can be used */ + struct file *dfd = vdev_open_fd(virt_dev, virt_dev->dif_filename, + virt_dev->rd_only); + if (IS_ERR(dfd)) { + res = PTR_ERR(dfd); + goto out; + } + filp_close(dfd, NULL); + } + + res = scst_set_dif_params(dev, virt_dev->dif_mode, virt_dev->dif_type); + if (res != 0) + goto out; + + if (virt_dev->dif_type != 2) + scst_dev_set_dif_static_app_tag_combined(dev, + virt_dev->dif_static_app_tag_combined); + else if (virt_dev->dif_static_app_tag_combined != SCST_DIF_NO_CHECK_APP_TAG) + PRINT_WARNING("Device %s: static app tag is ignored for DIF " + "mode 2", dev->virt_name); + +#ifndef CONFIG_SCST_PROC + if (virt_dev->dif_filename != NULL) { + res = scst_create_dev_attr(dev, &vdev_dif_filename_attr); + if (res != 0) { + PRINT_ERROR("Can't create attr %s for dev %s", + vdev_dif_filename_attr.attr.name, + dev->virt_name); + goto out; + } + } +#endif + if (virt_dev->zero_copy && virt_dev->o_direct_flag) { PRINT_ERROR("%s: combining zero_copy with o_direct is not" " supported", virt_dev->filename); @@ -1420,6 +1635,13 @@ static int vdisk_attach(struct scst_device *dev) dev->tas = DEF_TAS; dev->tas_saved = DEF_TAS; dev->tas_default = DEF_TAS; + dev->dpicz = DEF_DPICZ; + dev->dpicz_saved = DEF_DPICZ; + dev->dpicz_default = DEF_DPICZ; + if ((virt_dev->dif_filename == NULL) && !virt_dev->blk_integrity) + dev->ato = SCST_ATO_0_MODIFIED_BY_STORAGE; + else + dev->ato = SCST_ATO_1_NOT_MODIFIED_BY_STORAGE; if (vdev_saved_mode_pages_enabled) vdev_load_mode_pages(virt_dev); @@ -1457,7 +1679,7 @@ static int vdisk_open_fd(struct scst_vdisk_dev *virt_dev, bool read_only) lockdep_assert_held(&scst_mutex); sBUG_ON(!virt_dev->filename); - virt_dev->fd = vdev_open_fd(virt_dev, read_only); + virt_dev->fd = vdev_open_fd(virt_dev, virt_dev->filename, read_only); if (IS_ERR(virt_dev->fd)) { res = PTR_ERR(virt_dev->fd); virt_dev->fd = NULL; @@ -1465,12 +1687,27 @@ static int vdisk_open_fd(struct scst_vdisk_dev *virt_dev, bool read_only) virt_dev->filename, res); goto out; } - virt_dev->bdev = virt_dev->blockio ? - virt_dev->fd->f_dentry->d_inode->i_bdev : NULL; + virt_dev->bdev = virt_dev->blockio ? file_inode(virt_dev->fd)->i_bdev : + NULL; res = 0; + if (virt_dev->dif_filename != NULL) { + virt_dev->dif_fd = vdev_open_fd(virt_dev, + virt_dev->dif_filename, read_only); + if (IS_ERR(virt_dev->dif_fd)) { + res = PTR_ERR(virt_dev->dif_fd); + virt_dev->dif_fd = NULL; + goto out_close_fd; + } + } + out: return res; + +out_close_fd: + filp_close(virt_dev->fd, NULL); + virt_dev->fd = NULL; + goto out; } static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev) @@ -1482,6 +1719,10 @@ static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev) virt_dev->fd = NULL; virt_dev->bdev = NULL; } + if (virt_dev->dif_fd) { + filp_close(virt_dev->dif_fd, NULL); + virt_dev->dif_fd = NULL; + } } /* Invoked with scst_mutex held, so no further locking is necessary here. */ @@ -1503,8 +1744,10 @@ static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev) virt_dev->tgt_dev_cnt--; goto out; } - } else + } else { virt_dev->fd = NULL; + virt_dev->dif_fd = NULL; + } out: TRACE_EXIT_RES(res); @@ -1618,6 +1861,119 @@ static enum compl_status_e vdisk_exec_send_diagnostic(struct vdisk_cmd_params *p return CMD_SUCCEEDED; } +static int vdisk_format_dif(struct scst_cmd *cmd, uint64_t start_lba, + uint64_t blocks) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + loff_t loff; + mm_segment_t old_fs; + loff_t err = 0; + ssize_t full_len; + struct file *fd = virt_dev->dif_fd; + struct iovec *iv; + int max_iv_count, iv_count, i; + struct page *iv_page, *data_page; + uint8_t *data_buf; + int64_t left, done; + + TRACE_ENTRY(); + + if (virt_dev->dif_fd == NULL) + goto out; + + EXTRACHECKS_BUG_ON(!(dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE)); + + iv_page = alloc_page(GFP_KERNEL); + if (iv_page == NULL) { + PRINT_ERROR("Unable to allocate iv page"); + scst_set_busy(cmd); + res = -ENOMEM; + goto out; + } + + data_page = alloc_page(GFP_KERNEL); + if (data_page == NULL) { + PRINT_ERROR("Unable to allocate tags data page"); + scst_set_busy(cmd); + res = -ENOMEM; + goto out_free_iv; + } + + data_buf = page_address(data_page); + memset(data_buf, 0xFF, PAGE_SIZE); + + iv = page_address(iv_page); + max_iv_count = min_t(int, UIO_MAXIOV, (int)PAGE_SIZE/sizeof(*iv)); + + for (i = 0; i < max_iv_count; i++) + iv[i].iov_base = (uint8_t __force __user *)data_buf; + + old_fs = get_fs(); + set_fs(get_ds()); + + loff = start_lba << SCST_DIF_TAG_SHIFT; + left = blocks << SCST_DIF_TAG_SHIFT; + done = 0; + while (left > 0) { + iv_count = 0; + full_len = 0; + i = -1; + while (1) { + int len = min_t(size_t, (size_t)left, PAGE_SIZE); + full_len += len; + i++; + iv_count++; + iv[i].iov_len = len; + left -= len; + done += len; + EXTRACHECKS_BUG_ON(left < 0); + if ((iv_count == max_iv_count) || (left == 0)) + break; + } + + TRACE_DBG("Formatting DIF: full_len %zd, off %lld", + full_len, (long long)loff); + + /* WRITE */ + err = vfs_writev(fd, (struct iovec __force __user *)iv, iv_count, + &loff); + if (err < 0) { + PRINT_ERROR("Formatting DIF write() returned %lld from " + "%zd", (long long unsigned int)err, full_len); + if (err == -EAGAIN) + scst_set_busy(cmd); + else + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_write_error)); + res = err; + goto out_set_fs; + } else if (err < full_len) { + /* + * Probably that's wrong, but sometimes write() returns + * value less, than requested. Let's restart. + */ + left += full_len - err; + done -= full_len - err; + } + + virt_dev->format_progress_done = done; + }; + +out_set_fs: + set_fs(old_fs); + + __free_page(data_page); + +out_free_iv: + __free_page(iv_page); + +out: + TRACE_EXIT_RES(res); + return res; +} + static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) { int res = CMD_SUCCEEDED; @@ -1625,7 +1981,7 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) struct scst_device *dev = cmd->dev; struct scst_vdisk_dev *virt_dev = dev->dh_priv; uint8_t *buf; - int prot_type = 0, pinfo; + int prot_type = dev->dev_dif_type, pinfo; bool immed = false; TRACE_ENTRY(); @@ -1751,7 +2107,7 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) TRACE_DBG("prot_type %d, pinfo %d, immed %d (cmd %p)", prot_type, pinfo, immed, cmd); - if (prot_type != 0) { + if (prot_type != dev->dev_dif_type) { PRINT_ERROR("FORMAT UNIT: DIF type %d not supported (dev %s)", prot_type, dev->virt_name); scst_set_invalid_field_in_cdb(cmd, 1, @@ -1771,7 +2127,7 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) spin_unlock(&virt_dev->flags_lock); virt_dev->format_progress_done = 0; - virt_dev->format_progress_to_do = 100; + virt_dev->format_progress_to_do = virt_dev->nblocks << SCST_DIF_TAG_SHIFT; if (virt_dev->thin_provisioned) { int rc = vdisk_unmap_range(cmd, virt_dev, 0, virt_dev->nblocks); @@ -1779,6 +2135,9 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) goto finished; } + if (pinfo != 0) + vdisk_format_dif(cmd, 0, virt_dev->nblocks); + finished: spin_lock(&virt_dev->flags_lock); virt_dev->format_active = 0; @@ -1802,9 +2161,9 @@ static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) return INVALID_OPCODE; } -#define VDEV_DEF_RDPROTECT 0 -#define VDEV_DEF_WRPROTECT 0 -#define VDEV_DEF_VRPROTECT 0 +#define VDEV_DEF_RDPROTECT 0xE0 +#define VDEV_DEF_WRPROTECT 0xE0 +#define VDEV_DEF_VRPROTECT 0xE0 #define VDEF_DEF_GROUP_NUM 0 @@ -1896,6 +2255,22 @@ static const struct scst_opcode_descriptor scst_op_descr_read16 = { SCST_OD_DEFAULT_CONTROL_BYTE }, }; +static const struct scst_opcode_descriptor scst_op_descr_read32 = { + .od_opcode = VARIABLE_LENGTH_CMD, + .od_serv_action = SUBCODE_READ_32, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 32, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VARIABLE_LENGTH_CMD, SCST_OD_DEFAULT_CONTROL_BYTE, + 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, 0x18, 0, + SUBCODE_READ_32, VDEV_DEF_RDPROTECT | 0x18, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF }, +}; + static const struct scst_opcode_descriptor scst_op_descr_read_capacity = { .od_opcode = READ_CAPACITY, .od_support = 3, /* supported as in the standard */ @@ -1996,6 +2371,22 @@ static const struct scst_opcode_descriptor scst_op_descr_verify16 = { SCST_OD_DEFAULT_CONTROL_BYTE }, }; +static const struct scst_opcode_descriptor scst_op_descr_verify32 = { + .od_opcode = VARIABLE_LENGTH_CMD, + .od_serv_action = SUBCODE_VERIFY_32, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 32, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VARIABLE_LENGTH_CMD, SCST_OD_DEFAULT_CONTROL_BYTE, + 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, 0x18, 0, + SUBCODE_VERIFY_32, VDEV_DEF_VRPROTECT | 0x16, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF }, +}; + static const struct scst_opcode_descriptor scst_op_descr_write6 = { .od_opcode = WRITE_6, .od_support = 3, /* supported as in the standard */ @@ -2040,6 +2431,22 @@ static const struct scst_opcode_descriptor scst_op_descr_write16 = { SCST_OD_DEFAULT_CONTROL_BYTE }, }; +static const struct scst_opcode_descriptor scst_op_descr_write32 = { + .od_opcode = VARIABLE_LENGTH_CMD, + .od_serv_action = SUBCODE_WRITE_32, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 32, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VARIABLE_LENGTH_CMD, SCST_OD_DEFAULT_CONTROL_BYTE, + 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, 0x18, 0, + SUBCODE_WRITE_32, VDEV_DEF_WRPROTECT | 0x1A, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF }, +}; + static const struct scst_opcode_descriptor scst_op_descr_write_verify10 = { .od_opcode = WRITE_VERIFY, .od_support = 3, /* supported as in the standard */ @@ -2074,6 +2481,22 @@ static const struct scst_opcode_descriptor scst_op_descr_write_verify16 = { SCST_OD_DEFAULT_CONTROL_BYTE }, }; +static const struct scst_opcode_descriptor scst_op_descr_write_verify32 = { + .od_opcode = VARIABLE_LENGTH_CMD, + .od_serv_action = SUBCODE_WRITE_VERIFY_32, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 32, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VARIABLE_LENGTH_CMD, SCST_OD_DEFAULT_CONTROL_BYTE, + 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, 0x18, 0, + SUBCODE_WRITE_VERIFY_32, VDEV_DEF_WRPROTECT | 0x16, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF }, +}; + static const struct scst_opcode_descriptor scst_op_descr_write_same10 = { .od_opcode = WRITE_SAME, .od_support = 3, /* supported as in the standard */ @@ -2097,6 +2520,22 @@ static const struct scst_opcode_descriptor scst_op_descr_write_same16 = { SCST_OD_DEFAULT_CONTROL_BYTE }, }; +static const struct scst_opcode_descriptor scst_op_descr_write_same32 = { + .od_opcode = VARIABLE_LENGTH_CMD, + .od_serv_action = SUBCODE_WRITE_SAME_32, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 32, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VARIABLE_LENGTH_CMD, SCST_OD_DEFAULT_CONTROL_BYTE, + 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, 0x18, 0, + SUBCODE_WRITE_SAME_32, VDEV_DEF_WRPROTECT | 0x8, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF }, +}; + static const struct scst_opcode_descriptor scst_op_descr_read_toc = { .od_opcode = READ_TOC, .od_support = 3, /* supported as in the standard */ @@ -2164,6 +2603,14 @@ static const struct scst_opcode_descriptor scst_op_descr_read_toc = { &scst_op_descr_verify12, \ &scst_op_descr_verify16, +static const vdisk_op_fn blockio_var_len_ops[] = { + [SUBCODE_READ_32] = blockio_exec_read, + [SUBCODE_WRITE_32] = blockio_exec_write, + [SUBCODE_WRITE_VERIFY_32] = blockio_exec_write_verify, + [SUBCODE_VERIFY_32] = vdev_exec_verify, + [SUBCODE_WRITE_SAME_32] = vdisk_exec_write_same, +}; + static vdisk_op_fn blockio_ops[256] = { [READ_6] = blockio_exec_read, [READ_10] = blockio_exec_read, @@ -2176,12 +2623,21 @@ static vdisk_op_fn blockio_ops[256] = { [WRITE_VERIFY] = blockio_exec_write_verify, [WRITE_VERIFY_12] = blockio_exec_write_verify, [WRITE_VERIFY_16] = blockio_exec_write_verify, + [VARIABLE_LENGTH_CMD] = blockio_exec_var_len_cmd, [VERIFY] = vdev_exec_verify, [VERIFY_12] = vdev_exec_verify, [VERIFY_16] = vdev_exec_verify, SHARED_OPS }; +static const vdisk_op_fn fileio_var_len_ops[] = { + [SUBCODE_READ_32] = fileio_exec_read, + [SUBCODE_WRITE_32] = fileio_exec_write, + [SUBCODE_WRITE_VERIFY_32] = fileio_exec_write_verify, + [SUBCODE_VERIFY_32] = vdev_exec_verify, + [SUBCODE_WRITE_SAME_32] = vdisk_exec_write_same, +}; + static vdisk_op_fn fileio_ops[256] = { [READ_6] = fileio_exec_read, [READ_10] = fileio_exec_read, @@ -2194,12 +2650,20 @@ static vdisk_op_fn fileio_ops[256] = { [WRITE_VERIFY] = fileio_exec_write_verify, [WRITE_VERIFY_12] = fileio_exec_write_verify, [WRITE_VERIFY_16] = fileio_exec_write_verify, + [VARIABLE_LENGTH_CMD] = fileio_exec_var_len_cmd, [VERIFY] = vdev_exec_verify, [VERIFY_12] = vdev_exec_verify, [VERIFY_16] = vdev_exec_verify, SHARED_OPS }; +static const vdisk_op_fn nullio_var_len_ops[] = { + [SUBCODE_READ_32] = nullio_exec_read, + [SUBCODE_WRITE_32] = nullio_exec_write, + [SUBCODE_WRITE_VERIFY_32] = nullio_exec_write_verify, + [SUBCODE_WRITE_SAME_32] = vdisk_exec_write_same, +}; + static vdisk_op_fn nullio_ops[256] = { [READ_6] = nullio_exec_read, [READ_10] = nullio_exec_read, @@ -2212,6 +2676,7 @@ static vdisk_op_fn nullio_ops[256] = { [WRITE_VERIFY] = nullio_exec_write_verify, [WRITE_VERIFY_12] = nullio_exec_write_verify, [WRITE_VERIFY_16] = nullio_exec_write_verify, + [VARIABLE_LENGTH_CMD] = nullio_exec_var_len_cmd, [VERIFY] = nullio_exec_verify, [VERIFY_12] = nullio_exec_verify, [VERIFY_16] = nullio_exec_verify, @@ -2233,6 +2698,17 @@ static const struct scst_opcode_descriptor *vdisk_opcode_descriptors[] = { SCST_OPCODE_DESCRIPTORS }; +static const struct scst_opcode_descriptor *vdisk_opcode_descriptors_type2[] = { + SHARED_OPCODE_DESCRIPTORS + VDISK_OPCODE_DESCRIPTORS + &scst_op_descr_read32, + &scst_op_descr_write32, + &scst_op_descr_verify32, + &scst_op_descr_write_verify32, + &scst_op_descr_write_same32, + SCST_OPCODE_DESCRIPTORS +}; + static const struct scst_opcode_descriptor *vcdrom_opcode_descriptors[] = { SHARED_OPCODE_DESCRIPTORS &scst_op_descr_allow_medium_removal, @@ -2244,8 +2720,13 @@ static int vdisk_get_supported_opcodes(struct scst_cmd *cmd, const struct scst_opcode_descriptor ***out_supp_opcodes, int *out_supp_opcodes_cnt) { - *out_supp_opcodes = vdisk_opcode_descriptors; - *out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors); + if (cmd->dev->dev_dif_type != 2) { + *out_supp_opcodes = vdisk_opcode_descriptors; + *out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors); + } else { + *out_supp_opcodes = vdisk_opcode_descriptors_type2; + *out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors); + } return 0; } @@ -2266,6 +2747,10 @@ static bool vdisk_use_zero_copy(const struct scst_cmd *cmd) return false; switch (cmd->cdb[0]) { + case VARIABLE_LENGTH_CMD: + if (cmd->cdb[9] != SUBCODE_READ_32) + break; + /* else go througth */ case READ_6: case READ_10: case READ_12: @@ -2351,6 +2836,15 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) } switch (opcode) { + case VARIABLE_LENGTH_CMD: + if (cmd->cdb[9] == SUBCODE_WRITE_32) { + fua = (cdb[10] & 0x8); + if (fua) + TRACE(TRACE_ORDER, "FUA: loff=%lld, data_len=%lld", + (long long unsigned int)loff, + (long long unsigned int)data_len); + } + break; case WRITE_10: case WRITE_12: case WRITE_16: @@ -3089,8 +3583,9 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) sector_t start_sector = start_lba << (cmd->dev->block_shift - 9); sector_t nr_sects = blocks << (cmd->dev->block_shift - 9); - struct inode *inode = fd->f_dentry->d_inode; + struct inode *inode = file_inode(fd); gfp_t gfp = cmd->cmd_gfp_mask; + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31) err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp); #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) \ @@ -3127,6 +3622,12 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, goto out; } + if (virt_dev->dif_fd != NULL) { + res = vdisk_format_dif(cmd, start_lba, blocks); + if (unlikely(res != 0)) + goto out; + } + success: res = 0; @@ -3155,7 +3656,7 @@ static void vdisk_exec_write_same_unmap(struct vdisk_cmd_params *p) PRINT_WARNING("Invalid WRITE SAME data len %lld (max allowed " "%lld)", (long long)cmd->data_len, (long long)cmd->dev->max_write_same_len); - scst_set_invalid_field_in_cdb(cmd, cmd->len_off, 0); + scst_set_invalid_field_in_cdb(cmd, cmd->len_off, 0); goto out; } @@ -3195,36 +3696,29 @@ static enum compl_status_e vdisk_exec_write_same(struct vdisk_cmd_params *p) { struct scst_cmd *cmd = p->cmd; enum compl_status_e res = CMD_SUCCEEDED; + uint8_t ctrl_offs = (cmd->cdb_len < 32) ? 1 : 10; TRACE_ENTRY(); - if (unlikely(cmd->cdb[1] & 1)) { + if (unlikely(cmd->cdb[ctrl_offs] & 1)) { TRACE_DBG("%s", "ANCHOR not supported"); - scst_set_invalid_field_in_cdb(cmd, 1, + scst_set_invalid_field_in_cdb(cmd, ctrl_offs, SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); goto out; } - if (unlikely(cmd->cdb[1] & 0xE0)) { - TRACE_DBG("%s", "WRPROTECT not supported"); - scst_set_invalid_field_in_cdb(cmd, 1, - SCST_INVAL_FIELD_BIT_OFFS_VALID | 5); - goto out; - } - if (unlikely(cmd->data_len <= 0)) { scst_set_invalid_field_in_cdb(cmd, cmd->len_off, 0); goto out; } - if (cmd->cdb[1] & 0x8) { + if (cmd->cdb[ctrl_offs] & 0x8) vdisk_exec_write_same_unmap(p); - goto out; + else { + scst_write_same(cmd); + res = RUNNING_ASYNC; } - scst_write_same(cmd); - res = RUNNING_ASYNC; - out: TRACE_EXIT_RES(res); return res; @@ -3454,8 +3948,31 @@ static int vdisk_dev_id_vpd(uint8_t *buf, struct scst_cmd *cmd, static int vdisk_ext_inq(uint8_t *buf, struct scst_cmd *cmd, struct scst_vdisk_dev *virt_dev) { + struct scst_device *dev = cmd->dev; + buf[1] = 0x86; buf[3] = 0x3C; + if (dev->dev_dif_mode != SCST_DIF_MODE_NONE) { + switch (dev->dev_dif_type) { + case 1: + buf[4] = 0; /* SPT=0, type 1 only supported */ + break; + case 2: + buf[4] = 0x10; /* SPT=010, type 2 only supported */ + break; + case 3: + buf[4] = 0x20; /* SPT=100, type 3 only supported */ + break; + default: + sBUG_ON(1); + break; + } + buf[4] |= 4; /* GRD_CHK */ + if (dev->dif_app_chk) + buf[4] |= 2; /* APP_CHK */ + if (dev->dif_ref_chk) + buf[4] |= 1; /* REF_CHK */ + } buf[5] = 7; /* HEADSUP=1, ORDSUP=1, SIMPSUP=1 */ buf[6] = (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : 1; /* V_SUP */ buf[7] = 1; /* LUICLR=1 */ @@ -3553,8 +4070,10 @@ static int vdisk_inq(uint8_t *buf, struct scst_cmd *cmd, if (cmd->tgtt->fake_aca) buf[3] |= 0x20; buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */ + if (cmd->dev->dev_dif_mode != SCST_DIF_MODE_NONE) + buf[5] |= 1; /* PROTECT */ if (scst_impl_alua_configured(cmd->dev)) - buf[5] = SCST_INQ_TPGS_MODE_IMPLICIT; + buf[5] |= SCST_INQ_TPGS_MODE_IMPLICIT; buf[6] = 0x10; /* MultiP 1 */ buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */ @@ -3897,12 +4416,14 @@ static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, switch (pcontrol) { case 0: /* current */ p[2] |= virt_dev->dev->tst << 5; - p[2] |= virt_dev->dev->tmf_only << 4; p[2] |= virt_dev->dev->d_sense << 2; + p[2] |= virt_dev->dev->dpicz << 3; + p[2] |= virt_dev->dev->tmf_only << 4; p[3] |= virt_dev->dev->queue_alg << 4; p[3] |= virt_dev->dev->qerr << 1; p[4] |= virt_dev->dev->swp << 3; p[5] |= virt_dev->dev->tas << 6; + p[5] |= virt_dev->dev->ato << 7; break; case 1: /* changeable */ memset(p + 2, 0, sizeof(ctrl_m_pg) - 2); @@ -3914,29 +4435,35 @@ static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, p[2] |= 7 << 5; /* TST */ #endif p[2] |= 1 << 2; /* D_SENSE */ + p[2] |= 1 << 3; /* DPICZ */ p[2] |= 1 << 4; /* TMF_ONLY */ p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ p[3] |= 3 << 1; /* QErr */ p[4] |= 1 << 3; /* SWP */ p[5] |= 1 << 6; /* TAS */ + p[5] |= 0 << 7; /* ATO */ break; case 2: /* default */ p[2] |= virt_dev->tst << 5; p[2] |= virt_dev->dev->d_sense_default << 2; + p[2] |= virt_dev->dev->dpicz_default << 3; p[2] |= virt_dev->dev->tmf_only_default << 4; p[3] |= virt_dev->dev->queue_alg_default << 4; p[3] |= virt_dev->dev->qerr_default << 1; p[4] |= virt_dev->dev->swp_default << 3; p[5] |= virt_dev->dev->tas_default << 6; + p[5] |= virt_dev->dev->ato << 7; break; case 3: /* saved */ p[2] |= virt_dev->dev->tst << 5; p[2] |= virt_dev->dev->d_sense_saved << 2; + p[2] |= virt_dev->dev->dpicz_saved << 3; p[2] |= virt_dev->dev->tmf_only_default << 4; p[3] |= virt_dev->dev->queue_alg_saved << 4; p[3] |= virt_dev->dev->qerr_saved << 1; p[4] |= virt_dev->dev->swp_saved << 3; p[5] |= virt_dev->dev->tas_saved << 6; + p[5] |= virt_dev->dev->ato << 7; break; default: sBUG(); @@ -4115,7 +4642,7 @@ out_not_sup: static int vdisk_set_wt(struct scst_vdisk_dev *virt_dev, int wt, bool read_only) { int res = 0; - struct file *fd; + struct file *fd, *dif_fd = NULL; bool old_wt = virt_dev->wt_flag; TRACE_ENTRY(); @@ -4132,25 +4659,40 @@ static int vdisk_set_wt(struct scst_vdisk_dev *virt_dev, int wt, bool read_only) * to reopen fd. */ - fd = vdev_open_fd(virt_dev, read_only); + fd = vdev_open_fd(virt_dev, virt_dev->filename, read_only); if (IS_ERR(fd)) { - PRINT_ERROR("filp_open(%s) returned an error %ld", - virt_dev->filename, PTR_ERR(fd)); - spin_lock(&virt_dev->flags_lock); - virt_dev->wt_flag = old_wt; - spin_unlock(&virt_dev->flags_lock); res = PTR_ERR(fd); - goto out; + goto out_err; + } + + if (virt_dev->dif_filename != NULL) { + dif_fd = vdev_open_fd(virt_dev, virt_dev->dif_filename, read_only); + if (IS_ERR(dif_fd)) { + res = PTR_ERR(dif_fd); + goto out_err_close_fd; + } } if (virt_dev->fd) filp_close(virt_dev->fd, NULL); + if (virt_dev->dif_fd) + filp_close(virt_dev->dif_fd, NULL); virt_dev->fd = fd; + virt_dev->dif_fd = dif_fd; out: TRACE_EXIT_RES(res); return res; + +out_err_close_fd: + filp_close(fd, NULL); + +out_err: + spin_lock(&virt_dev->flags_lock); + virt_dev->wt_flag = old_wt; + spin_unlock(&virt_dev->flags_lock); + goto out; } static void vdisk_ctrl_m_pg_select(unsigned char *p, @@ -4159,9 +4701,9 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, { struct scst_device *dev = virt_dev->dev; int old_swp = dev->swp, old_tas = dev->tas, old_dsense = dev->d_sense; - int old_queue_alg = dev->queue_alg; + int old_queue_alg = dev->queue_alg, old_dpicz = dev->dpicz; int rc, old_tmf_only = dev->tmf_only, old_qerr = dev->qerr; - int queue_alg, swp, tas, tmf_only, qerr, d_sense; + int queue_alg, swp, tas, tmf_only, qerr, d_sense, dpicz; TRACE_ENTRY(); @@ -4232,6 +4774,16 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, goto out; } + dpicz = (p[2] & 0x8) >> 3; + if (dpicz > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page " + "dpicz value %d (initiator %s, dev %s)", dpicz, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); + goto out; + } + qerr = (p[3] & 0x6) >> 1; if ((qerr == SCST_QERR_2_RESERVED) || (qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) { @@ -4257,12 +4809,14 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, dev->swp = swp; dev->tas = tas; dev->tmf_only = tmf_only; + dev->dpicz = dpicz; dev->qerr = qerr; dev->d_sense = d_sense; if ((dev->swp == old_swp) && (dev->tas == old_tas) && (dev->d_sense == old_dsense) && (dev->queue_alg == old_queue_alg) && - (dev->qerr == old_qerr) && (dev->tmf_only == old_tmf_only)) + (dev->qerr == old_qerr) && (dev->tmf_only == old_tmf_only) && + (dev->dpicz == old_dpicz)) goto out; if (!save) @@ -4276,6 +4830,7 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, dev->queue_alg = old_queue_alg; dev->tmf_only = old_tmf_only; dev->qerr = old_qerr; + dev->dpicz = old_dpicz; /* Hopefully, the error is temporary */ scst_set_busy(cmd); goto out; @@ -4287,14 +4842,16 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, dev->queue_alg_saved = dev->queue_alg; dev->tmf_only_saved = dev->tmf_only; dev->qerr_saved = dev->qerr; + dev->dpicz_saved = dev->dpicz; out_ok: PRINT_INFO("Device %s: new control mode page parameters: SWP %x " "(was %x), TAS %x (was %x), TMF_ONLY %d (was %x), QErr %x " - "(was %x), D_SENSE %d (was %d), QUEUE ALG %d (was %d)", - virt_dev->name, dev->swp, old_swp, dev->tas, old_tas, - dev->tmf_only, old_tmf_only, dev->qerr, old_qerr, - dev->d_sense, old_dsense, dev->queue_alg, old_queue_alg); + "(was %x), D_SENSE %d (was %d), QUEUE ALG %d (was %d), " + "DPICZ %d (was %d)", virt_dev->name, dev->swp, old_swp, + dev->tas, old_tas, dev->tmf_only, old_tmf_only, dev->qerr, + old_qerr, dev->d_sense, old_dsense, dev->queue_alg, + old_queue_alg, dev->dpicz, old_dpicz); out: TRACE_EXIT(); @@ -4539,6 +5096,23 @@ static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p put_unaligned_be64(nblocks, &buffer[0]); put_unaligned_be32(blocksize, &buffer[8]); + if (cmd->dev->dev_dif_mode != SCST_DIF_MODE_NONE) { + switch (cmd->dev->dev_dif_type) { + case 1: + buffer[12] = 1; + break; + case 2: + buffer[12] = 3; + break; + case 3: + buffer[12] = 5; + break; + default: + sBUG_ON(1); + break; + } + } + switch (blocksize) { case 512: buffer[13] = 3; @@ -4728,12 +5302,11 @@ static enum compl_status_e vdisk_exec_prevent_allow_medium_removal(struct vdisk_ return CMD_SUCCEEDED; } -static int vdisk_fsync_blockio(loff_t loff, - loff_t len, struct scst_device *dev, gfp_t gfp_flags, - struct scst_cmd *cmd, bool async) +static int __vdisk_fsync_fileio(loff_t loff, + loff_t len, struct scst_device *dev, struct scst_cmd *cmd, + struct file *file) { int res; - struct scst_vdisk_dev *virt_dev = dev->dh_priv; TRACE_ENTRY(); @@ -4742,36 +5315,10 @@ static int vdisk_fsync_blockio(loff_t loff, ** anything without checking for NULL at first !!! **/ - EXTRACHECKS_BUG_ON(!virt_dev->blockio); - - res = vdisk_blockio_flush(virt_dev->bdev, gfp_flags, true, - cmd, async); - - TRACE_EXIT_RES(res); - return res; -} - -static int vdisk_fsync_fileio(loff_t loff, - loff_t len, struct scst_device *dev, struct scst_cmd *cmd, bool async) -{ - int res; - struct scst_vdisk_dev *virt_dev = dev->dh_priv; - struct file *file; - - TRACE_ENTRY(); - - /** - ** !!! CAUTION !!!: cmd can be NULL here! Don't use it for - ** anything without checking for NULL at first !!! - **/ - - EXTRACHECKS_BUG_ON(virt_dev->blockio); - - file = virt_dev->fd; + /* BLOCKIO can be here for DIF tags fsync */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) - res = sync_page_range(file->f_dentry->d_inode, file->f_mapping, - loff, len); + res = sync_page_range(file_inode(file), file->f_mapping, loff, len); #else #if 0 /* For sparse files we might need to sync metadata as well */ res = generic_write_sync(file, loff, len); @@ -4790,6 +5337,69 @@ static int vdisk_fsync_fileio(loff_t loff, } } + TRACE_EXIT_RES(res); + return res; +} + +static int vdisk_fsync_blockio(loff_t loff, + loff_t len, struct scst_device *dev, gfp_t gfp_flags, + struct scst_cmd *cmd, bool async) +{ + int res; + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + + TRACE_ENTRY(); + + /** + ** !!! CAUTION !!!: cmd can be NULL here! Don't use it for + ** anything without checking for NULL at first !!! + **/ + + EXTRACHECKS_BUG_ON(!virt_dev->blockio); + + /* Must be first, because vdisk_blockio_flush() can call scst_cmd_done()! */ + if (virt_dev->dif_fd != NULL) { + loff = (loff >> dev->block_shift) << SCST_DIF_TAG_SHIFT; + len = (len >> dev->block_shift) << SCST_DIF_TAG_SHIFT; + res = __vdisk_fsync_fileio(loff, len, dev, cmd, + virt_dev->dif_fd); + if (unlikely(res != 0)) + goto out; + } + + res = vdisk_blockio_flush(virt_dev->bdev, gfp_flags, true, + cmd, async); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int vdisk_fsync_fileio(loff_t loff, + loff_t len, struct scst_device *dev, struct scst_cmd *cmd, bool async) +{ + int res; + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + + TRACE_ENTRY(); + + /** + ** !!! CAUTION !!!: cmd can be NULL here! Don't use it for + ** anything without checking for NULL at first !!! + **/ + + res = __vdisk_fsync_fileio(loff, len, dev, cmd, virt_dev->fd); + if (unlikely(res != 0)) + goto done; + + if (virt_dev->dif_fd != NULL) { + loff = (loff >> dev->block_shift) << SCST_DIF_TAG_SHIFT; + len = (len >> dev->block_shift) << SCST_DIF_TAG_SHIFT; + res = __vdisk_fsync_fileio(loff, len, dev, cmd, + virt_dev->dif_fd); + } + +done: if (async) { if (cmd != NULL) { cmd->completed = 1; @@ -4888,10 +5498,302 @@ static enum compl_status_e nullio_exec_read(struct vdisk_cmd_params *p) } } + scst_dif_process_read(p->cmd); + TRACE_EXIT(); return CMD_SUCCEEDED; } +static int vdev_read_dif_tags(struct vdisk_cmd_params *p) +{ + int res = 0; + struct scst_cmd *cmd = p->cmd; + loff_t loff; + mm_segment_t old_fs; + loff_t err = 0; + ssize_t length, full_len; + uint8_t *address; + struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; + struct file *fd = virt_dev->dif_fd; + struct iovec *iv; + int iv_count, max_iv_count, i; + bool finished = false; + int tags_num, l; + struct scatterlist *tags_sg; + bool free_iv = false; + + TRACE_ENTRY(); + + /* + * !! Data for this cmd can be read simultaneously !! + */ + + EXTRACHECKS_BUG_ON(virt_dev->nullio); + +#if 0 /* no zero-copy (yet) */ + if (p->use_zero_copy) + goto out; +#endif + + EXTRACHECKS_BUG_ON(!(cmd->dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) || + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) == SCST_DIF_ACTION_NONE)); + + tags_num = (cmd->bufflen >> cmd->dev->block_shift); + if (unlikely(tags_num == 0)) + goto out; + + iv = p->iv; + if (iv == NULL) { + iv = vdisk_alloc_iv(cmd, p); + if (iv == NULL) { + unsigned long flags; + /* To protect sense setting against blockio data reads */ + spin_lock_irqsave(&vdev_err_lock, flags); + scst_set_busy(cmd); + spin_unlock_irqrestore(&vdev_err_lock, flags); + res = -ENOMEM; + goto out; + } + free_iv = true; + } + max_iv_count = p->iv_count; + + old_fs = get_fs(); + set_fs(get_ds()); + + tags_sg = NULL; + loff = (p->loff >> cmd->dev->block_shift) << SCST_DIF_TAG_SHIFT; + while (1) { + iv_count = 0; + full_len = 0; + i = -1; + address = scst_get_dif_buf(cmd, &tags_sg, &l); + length = l; + EXTRACHECKS_BUG_ON(length <= 0); + while (1) { + full_len += length; + i++; + iv_count++; + iv[i].iov_base = (uint8_t __force __user *)address; + iv[i].iov_len = length; + tags_num -= length >> SCST_DIF_TAG_SHIFT; + EXTRACHECKS_BUG_ON(tags_num < 0); + if ((iv_count == max_iv_count) || (tags_num == 0)) + break; + address = scst_get_dif_buf(cmd, &tags_sg, &l); + length = l; + EXTRACHECKS_BUG_ON(length <= 0); + } + if (tags_num == 0) + finished = true; + + TRACE_DBG("Reading DIF iv_count %d, full_len %zd, loff %lld", + iv_count, full_len, (long long)loff); + + /* READ */ + err = vfs_readv(fd, (struct iovec __force __user *)iv, iv_count, + &loff); + if ((err < 0) || (err < full_len)) { + unsigned long flags; + PRINT_ERROR("DIF readv() returned %lld from %zd " + "(offs %lld, dev %s)", (long long)err, + full_len, (long long)loff, cmd->dev->virt_name); + /* To protect sense setting with blockio */ + spin_lock_irqsave(&vdev_err_lock, flags); + if (err == -EAGAIN) + scst_set_busy(cmd); + else { + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_read_error)); + } + spin_unlock_irqrestore(&vdev_err_lock, flags); + res = err; + goto out_set_fs; + } + + for (i = 0; i < iv_count; i++) + scst_put_dif_buf(cmd, (void __force *)(iv[i].iov_base)); + + if (finished) + break; + }; + + set_fs(old_fs); + +out_free_iv: + if (free_iv && (iv != p->small_iv)) + kfree(p->iv); + +out: + TRACE_EXIT_RES(res); + return res; + +out_set_fs: + set_fs(old_fs); + for (i = 0; i < iv_count; i++) + scst_put_dif_buf(cmd, (void __force *)(iv[i].iov_base)); + goto out_free_iv; +} + +static int vdev_write_dif_tags(struct vdisk_cmd_params *p) +{ + int res = 0; + struct scst_cmd *cmd = p->cmd; + loff_t loff; + mm_segment_t old_fs; + loff_t err = 0; + ssize_t length, full_len; + uint8_t *address; + struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; + struct file *fd = virt_dev->dif_fd; + struct iovec *iv, *eiv; + int iv_count, eiv_count, max_iv_count, i; + bool finished = false; + int tags_num, l; + struct scatterlist *tags_sg; + bool free_iv = false; + + TRACE_ENTRY(); + + /* + * !! Data for this cmd can be written simultaneously !! + */ + + EXTRACHECKS_BUG_ON(virt_dev->nullio); + +#if 0 /* no zero-copy (yet) */ + if (p->use_zero_copy) + goto out; +#endif + + EXTRACHECKS_BUG_ON(!(cmd->dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) || + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) == SCST_DIF_ACTION_NONE)); + + tags_num = (cmd->bufflen >> cmd->dev->block_shift); + if (unlikely(tags_num == 0)) + goto out; + + iv = p->iv; + if (iv == NULL) { + iv = vdisk_alloc_iv(cmd, p); + if (iv == NULL) { + unsigned long flags; + /* To protect sense setting against blockio data writes */ + spin_lock_irqsave(&vdev_err_lock, flags); + scst_set_busy(cmd); + spin_unlock_irqrestore(&vdev_err_lock, flags); + res = -ENOMEM; + goto out; + } + free_iv = true; + } + max_iv_count = p->iv_count; + + old_fs = get_fs(); + set_fs(get_ds()); + + tags_sg = NULL; + loff = (p->loff >> cmd->dev->block_shift) << SCST_DIF_TAG_SHIFT; + while (1) { + iv_count = 0; + full_len = 0; + i = -1; + address = scst_get_dif_buf(cmd, &tags_sg, &l); + length = l; + EXTRACHECKS_BUG_ON(length <= 0); + while (1) { + full_len += length; + i++; + iv_count++; + iv[i].iov_base = (uint8_t __force __user *)address; + iv[i].iov_len = length; + tags_num -= length >> SCST_DIF_TAG_SHIFT; + EXTRACHECKS_BUG_ON(tags_num < 0); + if ((iv_count == max_iv_count) || (tags_num == 0)) + break; + address = scst_get_dif_buf(cmd, &tags_sg, &l); + length = l; + EXTRACHECKS_BUG_ON(length <= 0); + } + if (tags_num == 0) + finished = true; + + eiv = iv; + eiv_count = iv_count; +restart: + TRACE_DBG("Writing DIF: eiv_count %d, full_len %zd", eiv_count, full_len); + + /* WRITE */ + err = vfs_writev(fd, (struct iovec __force __user *)eiv, eiv_count, + &loff); + if (err < 0) { + unsigned long flags; + PRINT_ERROR("DIF write() returned %lld from %zd", + (long long unsigned int)err, full_len); + /* To protect sense setting with blockio */ + spin_lock_irqsave(&vdev_err_lock, flags); + if (err == -EAGAIN) + scst_set_busy(cmd); + else { + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_write_error)); + } + spin_unlock_irqrestore(&vdev_err_lock, flags); + res = err; + goto out_set_fs; + } else if (err < full_len) { + /* + * Probably that's wrong, but sometimes write() returns + * value less, than requested. Let's restart. + */ + int e = eiv_count; + TRACE_MGMT_DBG("DIF write() returned %d from %zd " + "(iv_count=%d)", (int)err, full_len, + eiv_count); + if (err == 0) { + PRINT_INFO("Suspicious: DIF write() returned 0 from " + "%zd (iv_count=%d)", full_len, eiv_count); + } + full_len -= err; + for (i = 0; i < e; i++) { + if ((long long)eiv->iov_len < err) { + err -= eiv->iov_len; + eiv++; + eiv_count--; + } else { + eiv->iov_base = + (uint8_t __force __user *)eiv->iov_base + err; + eiv->iov_len -= err; + break; + } + } + goto restart; + } + + for (i = 0; i < iv_count; i++) + scst_put_dif_buf(cmd, (void __force *)(iv[i].iov_base)); + + if (finished) + break; + }; + + set_fs(old_fs); + +out_free_iv: + if (free_iv && (iv != p->small_iv)) + kfree(iv); + +out: + TRACE_EXIT_RES(res); + return res; + +out_set_fs: + set_fs(old_fs); + for (i = 0; i < iv_count; i++) + scst_put_dif_buf(cmd, (void __force *)(iv[i].iov_base)); + goto out_free_iv; +} + static enum compl_status_e blockio_exec_read(struct vdisk_cmd_params *p) { blockio_exec_rw(p, false, false); @@ -4906,10 +5808,11 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p) loff_t err = 0; ssize_t length, full_len; uint8_t __user *address; - struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; + struct scst_device *dev = cmd->dev; + struct scst_vdisk_dev *virt_dev = dev->dh_priv; struct file *fd = virt_dev->fd; struct iovec *iv; - int iv_count, i; + int iv_count, i, max_iv_count; bool finished = false; TRACE_ENTRY(); @@ -4917,12 +5820,14 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p) EXTRACHECKS_BUG_ON(virt_dev->nullio); if (p->use_zero_copy) - goto out; + goto out_dif; iv = vdisk_alloc_iv(cmd, p); if (iv == NULL) goto out_nomem; + max_iv_count = p->iv_count; + length = scst_get_buf_first(cmd, (uint8_t __force **)&address); if (unlikely(length < 0)) { PRINT_ERROR("scst_get_buf_first() failed: %zd", length); @@ -4944,7 +5849,7 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p) iv_count++; iv[i].iov_base = address; iv[i].iov_len = length; - if (iv_count == UIO_MAXIOV) + if (iv_count == max_iv_count) break; length = scst_get_buf_next(cmd, (uint8_t __force **)&address); @@ -4960,12 +5865,11 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p) goto out_set_fs; } - TRACE_DBG("(iv_count %d, full_len %zd)", iv_count, full_len); + TRACE_DBG("Reading iv_count %d, full_len %zd", iv_count, full_len); /* READ */ err = vfs_readv(fd, (struct iovec __force __user *)iv, iv_count, &loff); - if ((err < 0) || (err < full_len)) { PRINT_ERROR("readv() returned %lld from %zd", (unsigned long long int)err, @@ -4990,6 +5894,16 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p) set_fs(old_fs); + if ((dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) && + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE)) { + err = vdev_read_dif_tags(p); + if (err != 0) + goto out; + } + +out_dif: + scst_dif_process_read(cmd); + out: TRACE_EXIT(); return CMD_SUCCEEDED; @@ -5008,6 +5922,7 @@ out_nomem: static enum compl_status_e nullio_exec_write(struct vdisk_cmd_params *p) { + scst_dif_process_write(p->cmd); return CMD_SUCCEEDED; } @@ -5016,14 +5931,67 @@ static enum compl_status_e blockio_exec_write(struct vdisk_cmd_params *p) struct scst_cmd *cmd = p->cmd; struct scst_device *dev = cmd->dev; struct scst_vdisk_dev *virt_dev = dev->dh_priv; + int res, rc; + + TRACE_ENTRY(); + + rc = scst_dif_process_write(cmd); + if (unlikely(rc != 0)) { + res = CMD_SUCCEEDED; + goto out; + } blockio_exec_rw(p, true, p->fua || virt_dev->wt_flag); - return RUNNING_ASYNC; + res = RUNNING_ASYNC; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static enum compl_status_e blockio_exec_var_len_cmd(struct vdisk_cmd_params *p) +{ + struct scst_cmd *cmd = p->cmd; + int res; + + TRACE_ENTRY(); + + res = blockio_var_len_ops[cmd->cdb[9]](p); + + TRACE_EXIT_RES(res); + return res; +} + +static enum compl_status_e nullio_exec_var_len_cmd(struct vdisk_cmd_params *p) +{ + struct scst_cmd *cmd = p->cmd; + int res; + + TRACE_ENTRY(); + + res = nullio_var_len_ops[cmd->cdb[9]](p); + + TRACE_EXIT_RES(res); + return res; +} + +static enum compl_status_e fileio_exec_var_len_cmd(struct vdisk_cmd_params *p) +{ + struct scst_cmd *cmd = p->cmd; + int res; + + TRACE_ENTRY(); + + res = fileio_var_len_ops[cmd->cdb[9]](p); + + TRACE_EXIT_RES(res); + return res; } static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p) { struct scst_cmd *cmd = p->cmd; + struct scst_device *dev = cmd->dev; loff_t loff = p->loff; mm_segment_t old_fs; loff_t err = 0; @@ -5032,13 +6000,17 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p) struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; struct file *fd = virt_dev->fd; struct iovec *iv, *eiv; - int i, iv_count, eiv_count; + int rc, i, iv_count, eiv_count, max_iv_count; bool finished = false; TRACE_ENTRY(); EXTRACHECKS_BUG_ON(virt_dev->nullio); + rc = scst_dif_process_write(cmd); + if (unlikely(rc != 0)) + goto out; + if (p->use_zero_copy) goto out_sync; @@ -5046,6 +6018,8 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p) if (iv == NULL) goto out_nomem; + max_iv_count = p->iv_count; + length = scst_get_buf_first(cmd, &address); if (unlikely(length < 0)) { PRINT_ERROR("scst_get_buf_first() failed: %zd", length); @@ -5067,7 +6041,7 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p) iv_count++; iv[i].iov_base = (uint8_t __force __user *)address; iv[i].iov_len = length; - if (iv_count == UIO_MAXIOV) + if (iv_count == max_iv_count) break; length = scst_get_buf_next(cmd, &address); } @@ -5085,12 +6059,11 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p) eiv = iv; eiv_count = iv_count; restart: - TRACE_DBG("writing(eiv_count %d, full_len %zd)", eiv_count, full_len); + TRACE_DBG("Writing: eiv_count %d, full_len %zd", eiv_count, full_len); /* WRITE */ err = vfs_writev(fd, (struct iovec __force __user *)eiv, eiv_count, &loff); - if (err < 0) { PRINT_ERROR("write() returned %lld from %zd", (unsigned long long int)err, @@ -5141,6 +6114,13 @@ restart: set_fs(old_fs); + if ((dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) && + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE)) { + err = vdev_write_dif_tags(p); + if (err != 0) + goto out; + } + out_sync: /* O_DSYNC flag is used for WT devices */ if (p->fua) @@ -5175,9 +6155,21 @@ static inline void blockio_check_finish(struct scst_blockio_work *blockio_work) { /* Decrement the bios in processing, and if zero signal completion */ if (atomic_dec_and_test(&blockio_work->bios_inflight)) { + struct scst_cmd *cmd = blockio_work->cmd; + + if ((cmd->data_direction & SCST_DATA_READ) && + likely(cmd->status == SAM_STAT_GOOD)) { + /* + * We, most likely, on interrupt, so defer DIF + * checking to later stage in thread context + */ + cmd->deferred_dif_read_check = 1; + } + blockio_work->cmd->completed = 1; - blockio_work->cmd->scst_cmd_done(blockio_work->cmd, + blockio_work->cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, scst_estimate_context()); + kmem_cache_free(blockio_work_cachep, blockio_work); } return; @@ -5214,14 +6206,16 @@ static void blockio_endio(struct bio *bio, int error) } if (unlikely(error != 0)) { - static DEFINE_SPINLOCK(blockio_endio_lock); unsigned long flags; PRINT_ERROR("BLOCKIO for cmd %p finished with error %d", blockio_work->cmd, error); - /* To protect from several bios finishing simultaneously */ - spin_lock_irqsave(&blockio_endio_lock, flags); + /* + * To protect from several bios finishing simultaneously + + * unsuccessful DIF tags reading/writing + */ + spin_lock_irqsave(&vdev_err_lock, flags); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) if (bio->bi_rw & (1 << BIO_RW)) @@ -5234,7 +6228,7 @@ static void blockio_endio(struct bio *bio, int error) scst_set_cmd_error(blockio_work->cmd, SCST_LOAD_SENSE(scst_sense_read_error)); - spin_unlock_irqrestore(&blockio_endio_lock, flags); + spin_unlock_irqrestore(&vdev_err_lock, flags); } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)) || (LINUX_VERSION_CODE > KERNEL_VERSION(3, 6, 0)) @@ -5295,12 +6289,117 @@ static void vdisk_bio_set_hoq(struct bio *bio) #endif } +#if defined(CONFIG_BLK_DEV_INTEGRITY) +static void vdisk_blk_add_dif(struct bio *bio, gfp_t gfp_mask, + const struct scst_device *dev, struct scatterlist **pdsg, + int *pdsg_offs, int *pdsg_len, bool last) +{ + int block_shift = dev->block_shift; + struct scatterlist *orig_dsg = *pdsg; + struct scatterlist *sg; + int sg_offs = *pdsg_offs, sg_len = *pdsg_len; + int pages, left, len, tags_len, rc; + struct bio_integrity_payload *bip; + + TRACE_ENTRY(); + + tags_len = ((bio_sectors(bio) << 9) >> block_shift) << SCST_DIF_TAG_SHIFT; + + TRACE_DBG("bio %p, tags_len %d, pdsg %p, pdsg_offs %d, pdsg_len %d, " + "last %d", bio, tags_len, *pdsg, *pdsg_offs, *pdsg_len, last); + + pages = 0; + left = tags_len; + sg = orig_dsg; + len = sg->length; + while (1) { + pages++; + left -= len; + if (left <= 0) { + if (!last) { + left = -left; + sg = __sg_next_inline(sg); + *pdsg = sg; + *pdsg_offs = sg->offset + left; + *pdsg_len = sg->length - left; + TRACE_DBG("left %d, pdsg %p, pdsg_offs %d, pdsg_len %d", + left, *pdsg, *pdsg_offs, *pdsg_len); + } + break; + } + sg = __sg_next_inline(sg); + len = sg->length; + } + + bip = bio_integrity_alloc(bio, gfp_mask, pages); + if (unlikely(bip == NULL)) { + PRINT_WARNING("Allocation of %d pages for DIF tags " + "failed! (dev %s)", pages, dev->virt_name); + goto out; /* proceed without integrity */ + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + bip->bip_iter.bi_size = tags_len; + bip->bip_iter.bi_sector = bio->bi_iter.bi_sector; +#else + bip->bip_size = tags_len; + bip->bip_sector = bio->bi_sector; +#endif + + left = tags_len; + sg = orig_dsg; + while (1) { + TRACE_DBG("page %p (buf %p), sg_len %d, sg_offs %d", + sg_page(sg), page_address(sg_page(sg)), + sg_len, sg_offs); + + rc = bio_integrity_add_page(bio, sg_page(sg), sg_len, sg_offs); + if (rc != sg_len) { + PRINT_WARNING("Can not add DIF tags page! " + "(dev %s)", dev->virt_name); + /* bio_integrity_free() will be called as part of bio_free() */ + goto out; /* proceed without integrity */ + } + + if (left < sg_len) { + TRACE_DBG("left %d, sg_len %d, sg_offs %d", + left, sg_len, sg_offs); + break; + } + + left -= sg_len; + EXTRACHECKS_BUG_ON(left < 0); + + TRACE_DBG("left %d", left); + + if (left == 0) + break; + + sg = __sg_next_inline(sg); + sg_len = sg->length; + sg_offs = sg->offset; + } + +out: + TRACE_EXIT(); + return; +} +#else /* defined(CONFIG_BLK_DEV_INTEGRITY) */ +static void vdisk_blk_add_dif(struct bio *bio, gfp_t gfp_mask, + const struct scst_device *dev, struct scatterlist **pdsg, + int *pdsg_offs, int *pdsg_len, bool last) +{ + BUG(); +} +#endif /* defined(CONFIG_BLK_DEV_INTEGRITY) */ + static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) { struct scst_cmd *cmd = p->cmd; u64 lba_start = scst_cmd_get_lba(cmd); - struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; - int block_shift = cmd->dev->block_shift; + struct scst_device *dev = cmd->dev; + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + int block_shift = dev->block_shift; struct block_device *bdev = virt_dev->bdev; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) struct bio_set *bs = virt_dev->vdisk_bioset; @@ -5316,11 +6415,21 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) struct blk_plug plug; #endif + struct scatterlist *dsg; + int dsg_offs, dsg_len; + bool dif = virt_dev->blk_integrity && + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE); TRACE_ENTRY(); WARN_ON(virt_dev->nullio); + if (dif) { + dsg = cmd->dif_sg; + dsg_offs = dsg->offset; + dsg_len = dsg->length; + } + /* Allocate and initialize blockio_work struct */ blockio_work = kmem_cache_alloc(blockio_work_cachep, gfp_mask); if (blockio_work == NULL) { @@ -5432,6 +6541,9 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) rc = bio_add_page(bio, pg, bytes, off); if (rc < bytes) { WARN_ON(rc != 0); + if (dif) + vdisk_blk_add_dif(bio, gfp_mask, dev, &dsg, + &dsg_offs, &dsg_len, false); need_new_bio = 1; lba_start0 += thislen >> block_shift; thislen = 0; @@ -5450,6 +6562,10 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) length = scst_get_sg_page_next(cmd, &page, &offset); } + if (dif) + vdisk_blk_add_dif(bio, gfp_mask, dev, &dsg, &dsg_offs, + &dsg_len, true); + /* +1 to prevent erroneous too early command completion */ atomic_set(&blockio_work->bios_inflight, bios+1); @@ -5461,6 +6577,7 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) bio = hbio; hbio = hbio->bi_next; bio->bi_next = NULL; + submit_bio((write != 0), bio); } @@ -5471,6 +6588,15 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) q->unplug_fn(q); #endif + if ((dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) && + (virt_dev->dif_fd != NULL) && + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE)) { + if (write) + vdev_write_dif_tags(p); + else + vdev_read_dif_tags(p); + } + blockio_check_finish(blockio_work); out: @@ -5796,6 +6922,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; uint8_t *mem_verify = NULL; int64_t data_len = scst_cmd_get_data_len(cmd); + enum scst_dif_actions checks = scst_get_dif_checks(cmd->cmd_dif_actions); TRACE_ENTRY(); @@ -5847,6 +6974,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) scst_put_buf(cmd, address_sav); goto out_free; } + if (compare && memcmp(address, mem_verify, len_mem) != 0) { TRACE_DBG("Verify: error memcmp length %zd", length); scst_set_cmd_error(cmd, @@ -5854,6 +6982,11 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) scst_put_buf(cmd, address_sav); goto out_free; } + + if (checks != 0) { + /* ToDo: check DIF tags as well */ + } + length -= len_mem; if (compare) address += len_mem; @@ -6014,6 +7147,7 @@ static enum compl_status_e fileio_exec_write_verify(struct vdisk_cmd_params *p) static enum compl_status_e nullio_exec_write_verify(struct vdisk_cmd_params *p) { + scst_dif_process_write(p->cmd); return CMD_SUCCEEDED; } @@ -6035,6 +7169,7 @@ static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd, dev->tmf_only = dev->tmf_only_saved; dev->d_sense = dev->d_sense_saved; + dev->dpicz = dev->dpicz_saved; dev->swp = dev->swp_saved; dev->tas = dev->tas_saved; dev->queue_alg = dev->queue_alg_saved; @@ -6118,6 +7253,18 @@ static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev) i += snprintf(&buf[i], buf_size - i, "%sTHIN_PROVISIONED", (j == i) ? "(" : ", "); + if (virt_dev->dif_mode != SCST_DIF_MODE_NONE) { + i += snprintf(&buf[i], buf_size - i, "%sDIF MODE %x, " + "DIF TYPE %d", (j == i) ? "(" : ", ", + virt_dev->dif_mode, virt_dev->dif_type); + if (virt_dev->dif_filename != NULL) + i += snprintf(&buf[i], buf_size - i, ", DIF FILENAME %s", + virt_dev->dif_filename); + else if (virt_dev->dif_static_app_tag_combined != SCST_DIF_NO_CHECK_APP_TAG) + i += snprintf(&buf[i], buf_size - i, ", DIF STATIC APP TAG %llx", + (long long)be64_to_cpu(virt_dev->dif_static_app_tag_combined)); + } + if (virt_dev->zero_copy) i += snprintf(&buf[i], buf_size - i, "%sZERO_COPY", (j == i) ? "(" : ", "); @@ -6190,10 +7337,25 @@ static int vdisk_create_bioset(struct scst_vdisk_dev *virt_dev) goto out; } + if (virt_dev->dif_mode & SCST_DIF_MODE_DEV) { + /* The same, pool size doesn't really matter */ + res = bioset_integrity_create(virt_dev->vdisk_bioset, 2); + if (res != 0) { + PRINT_ERROR("Failed to create integrity bioset " + "(dev %s)", virt_dev->name); + goto out_free; + } + } + res = 0; out: return res; + +out_free: + bioset_free(virt_dev->vdisk_bioset); + virt_dev->vdisk_bioset = NULL; + goto out; } static void vdisk_free_bioset(struct scst_vdisk_dev *virt_dev) @@ -6302,6 +7464,7 @@ static void vdev_destroy(struct scst_vdisk_dev *virt_dev) vdisk_free_bioset(virt_dev); #endif kfree(virt_dev->filename); + kfree(virt_dev->dif_filename); kfree(virt_dev); return; } @@ -6389,13 +7552,68 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, continue; } + if (!strcasecmp("dif_filename", p)) { + if (*pp != '/') { + PRINT_ERROR("DIF filename %s must be global " + "(device %s)", pp, virt_dev->name); + res = -EINVAL; + goto out; + } + + virt_dev->dif_filename = kstrdup(pp, GFP_KERNEL); + if (virt_dev->dif_filename == NULL) { + PRINT_ERROR("Unable to duplicate DIF filename %s " + "(device %s)", pp, virt_dev->name); + res = -ENOMEM; + goto out; + } + continue; + } + + if (!strcasecmp("dif_mode", p)) { + char *d = pp; + + while (1) { + char *dd; + + for (; (*d != '\0') && isspace(*d); d++) + ; + if (*d == '\0') + break; + + dd = strchr(d, '|'); + if (dd != NULL) + *dd = '\0'; + if (!strcasecmp(SCST_DIF_MODE_TGT_STR, d)) + virt_dev->dif_mode |= SCST_DIF_MODE_TGT; + else if (!strcasecmp(SCST_DIF_MODE_SCST_STR, d)) + virt_dev->dif_mode |= SCST_DIF_MODE_SCST; + else if (!strcasecmp(SCST_DIF_MODE_DEV_CHECK_STR, d)) { + virt_dev->dif_mode |= SCST_DIF_MODE_DEV_CHECK; + } else if (!strcasecmp(SCST_DIF_MODE_DEV_STORE_STR, d)) + virt_dev->dif_mode |= SCST_DIF_MODE_DEV_STORE; + else { + PRINT_ERROR("Error parsing DIF mode %s", pp); + res = -EINVAL; + goto out; + } + if (dd == NULL) + break; + else + *dd = '|'; + d = dd+1; + } + TRACE_DBG("DIF DEV mode %x", virt_dev->dif_mode); + continue; + } + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) res = kstrtoull(pp, 0, &val); #else res = strict_strtoull(pp, 0, &val); #endif if (res != 0) { - PRINT_ERROR("strtoul() for %s failed: %d (device %s)", + PRINT_ERROR("strtoull() for %s failed: %d (device %s)", pp, res, virt_dev->name); goto out; } @@ -6459,6 +7677,13 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } TRACE_DBG("block size %lld, block shift %d", val, virt_dev->blk_shift); + } else if (!strcasecmp("dif_type", p)) { + virt_dev->dif_type = val; + TRACE_DBG("DIF type %d", virt_dev->dif_type); + } else if (!strcasecmp("dif_static_app_tag", p)) { + virt_dev->dif_static_app_tag_combined = cpu_to_be64(val); + TRACE_DBG("DIF static app tag %llx", + (long long)be64_to_cpu(virt_dev->dif_static_app_tag_combined)); } else { PRINT_ERROR("Unknown parameter %s (device %s)", p, virt_dev->name); @@ -6545,7 +7770,9 @@ static int vdev_blockio_add_device(const char *device_name, char *params) int res = 0; const char *const allowed_params[] = { "filename", "read_only", "write_through", "removable", "blocksize", "nv_cache", - "rotational", "thin_provisioned", "tst", NULL }; + "rotational", "thin_provisioned", "tst", + "dif_mode", "dif_type", "dif_static_app_tag", + "dif_filename", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -6609,6 +7836,7 @@ static int vdev_nullio_add_device(const char *device_name, char *params) int res = 0; static const char *const allowed_params[] = { "read_only", "dummy", "removable", "blocksize", "rotational", + "dif_mode", "dif_type", "dif_static_app_tag", "size", "size_mb", "tst", NULL }; struct scst_vdisk_dev *virt_dev; @@ -8231,6 +9459,25 @@ static ssize_t vdev_zero_copy_show(struct kobject *kobj, return pos; } +static ssize_t vdev_dif_filename_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + pos = sprintf(buf, "%s\n%s", virt_dev->dif_filename, + (virt_dev->dif_filename != NULL) ? SCST_SYSFS_KEY_MARK "\n" : ""); + + TRACE_EXIT_RES(pos); + return pos; +} + #else /* CONFIG_SCST_PROC */ /* @@ -8993,6 +10240,8 @@ static int __init init_scst_vdisk_driver(void) { int res; + spin_lock_init(&vdev_err_lock); + init_ops(fileio_ops, ARRAY_SIZE(fileio_ops)); init_ops(blockio_ops, ARRAY_SIZE(blockio_ops)); init_ops(nullio_ops, ARRAY_SIZE(nullio_ops)); diff --git a/scst/src/scst_debug.c b/scst/src/scst_debug.c index 31632c163..40b8f54dd 100644 --- a/scst/src/scst_debug.c +++ b/scst/src/scst_debug.c @@ -1,9 +1,9 @@ /* * scst_debug.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Contains helper functions for execution tracing and error reporting. * Intended to be included in main .c file. diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 8b9b4e9fb..076d1e61b 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -1,9 +1,9 @@ /* * scst_lib.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -43,6 +45,10 @@ #include #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) +#include +#endif + #ifdef INSIDE_KERNEL_TREE #include #else @@ -400,7 +406,9 @@ static int get_cdb_info_verify16(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_len_1(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); -static int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, +static int get_cdb_info_lba_3_len_1_256_read(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops); +static int get_cdb_info_lba_3_len_1_256_write(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_bidi_lba_4_len_2(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); @@ -414,13 +422,19 @@ static int get_cdb_info_lba_2_none(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_lba_4_len_2(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); +static int get_cdb_info_read_10(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops); +static int get_cdb_info_lba_4_len_2_wrprotect(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops); static int get_cdb_info_lba_4_none(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); -static int get_cdb_info_lba_4_len_2(struct scst_cmd *cmd, +static int get_cdb_info_lba_4_len_4_rdprotect(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); -static int get_cdb_info_lba_4_len_4(struct scst_cmd *cmd, +static int get_cdb_info_lba_4_len_4_wrprotect(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); -static int get_cdb_info_lba_8_len_4(struct scst_cmd *cmd, +static int get_cdb_info_read_16(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops); +static int get_cdb_info_lba_8_len_4_wrprotect(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_lba_8_none(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); @@ -434,6 +448,8 @@ static int get_cdb_info_apt(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_min(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); +static int get_cdb_info_var_len(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops); /* +=====================================-============-======- @@ -593,7 +609,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_EXCL_ALLOWED, .info_lba_off = 1, .info_lba_len = 3, .info_len_off = 4, .info_len_len = 1, - .get_cdb_info = get_cdb_info_lba_3_len_1_256}, + .get_cdb_info = get_cdb_info_lba_3_len_1_256_read}, {.ops = 0x08, .devkey = " MV O OV ", .info_op_name = "READ(6)", .info_data_direction = SCST_DATA_READ, @@ -623,7 +639,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_MEDIUM, .info_lba_off = 1, .info_lba_len = 3, .info_len_off = 4, .info_len_len = 1, - .get_cdb_info = get_cdb_info_lba_3_len_1_256}, + .get_cdb_info = get_cdb_info_lba_3_len_1_256_write}, {.ops = 0x0A, .devkey = " M O OV ", .info_op_name = "WRITE(6)", .info_data_direction = SCST_DATA_WRITE, @@ -820,7 +836,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_EXCL_ALLOWED, .info_lba_off = 2, .info_lba_len = 4, .info_len_off = 7, .info_len_len = 2, - .get_cdb_info = get_cdb_info_lba_4_len_2}, + .get_cdb_info = get_cdb_info_read_10}, {.ops = 0x28, .devkey = " O ", .info_op_name = "GET MESSAGE(10)", .info_data_direction = SCST_DATA_READ, @@ -843,7 +859,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_MEDIUM, .info_lba_off = 2, .info_lba_len = 4, .info_len_off = 7, .info_len_len = 2, - .get_cdb_info = get_cdb_info_lba_4_len_2}, + .get_cdb_info = get_cdb_info_lba_4_len_2_wrprotect}, {.ops = 0x2A, .devkey = " O ", .info_op_name = "SEND MESSAGE(10)", .info_data_direction = SCST_DATA_WRITE, @@ -888,7 +904,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, .info_lba_off = 2, .info_lba_len = 4, .info_len_off = 7, .info_len_len = 2, - .get_cdb_info = get_cdb_info_lba_4_len_2}, + .get_cdb_info = get_cdb_info_lba_4_len_2_wrprotect}, {.ops = 0x2F, .devkey = "O OO O ", .info_op_name = "VERIFY(10)", .info_data_direction = SCST_DATA_UNKNOWN, @@ -1086,7 +1102,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { .info_op_flags = SCST_WRITE_MEDIUM, .info_lba_off = 2, .info_lba_len = 4, .info_len_off = 7, .info_len_len = 2, - .get_cdb_info = get_cdb_info_lba_4_len_2}, + .get_cdb_info = get_cdb_info_lba_4_len_2_wrprotect}, {.ops = 0x51, .devkey = " O ", .info_op_name = "READ DISC INFORMATION", .info_data_direction = SCST_DATA_READ, @@ -1187,6 +1203,13 @@ static const struct scst_sdbops scst_scsi_op_table[] = { .info_len_off = 5, .info_len_len = 4, .get_cdb_info = get_cdb_info_len_4}, + /* Variable length CDBs */ + {.ops = 0x7F, .devkey = "O ", + .info_op_name = "VAR LEN CDB", + .info_data_direction = SCST_DATA_UNKNOWN, + .info_op_flags = 0, + .get_cdb_info = get_cdb_info_var_len}, + /* 16-bytes length CDB */ {.ops = 0x80, .devkey = " O ", .info_op_name = "WRITE FILEMARKS(16)", @@ -1245,7 +1268,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_EXCL_ALLOWED, .info_lba_off = 2, .info_lba_len = 8, .info_len_off = 10, .info_len_len = 4, - .get_cdb_info = get_cdb_info_lba_8_len_4}, + .get_cdb_info = get_cdb_info_read_16}, {.ops = 0x89, .devkey = "O ", .info_op_name = "COMPARE AND WRITE", .info_data_direction = SCST_DATA_WRITE, @@ -1264,7 +1287,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_MEDIUM, .info_lba_off = 2, .info_lba_len = 8, .info_len_off = 10, .info_len_len = 4, - .get_cdb_info = get_cdb_info_lba_8_len_4}, + .get_cdb_info = get_cdb_info_lba_8_len_4_wrprotect}, {.ops = 0x8C, .devkey = " OOOOOOOOO ", .info_op_name = "READ ATTRIBUTE", .info_data_direction = SCST_DATA_READ, @@ -1283,7 +1306,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, .info_lba_off = 2, .info_lba_len = 8, .info_len_off = 10, .info_len_len = 4, - .get_cdb_info = get_cdb_info_lba_8_len_4}, + .get_cdb_info = get_cdb_info_lba_8_len_4_wrprotect}, {.ops = 0x8F, .devkey = "O OO O ", .info_op_name = "VERIFY(16)", .info_data_direction = SCST_DATA_UNKNOWN, @@ -1424,7 +1447,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_EXCL_ALLOWED, .info_lba_off = 2, .info_lba_len = 4, .info_len_off = 6, .info_len_len = 4, - .get_cdb_info = get_cdb_info_lba_4_len_4}, + .get_cdb_info = get_cdb_info_lba_4_len_4_rdprotect}, {.ops = 0xA9, .devkey = " O ", .info_op_name = "PLAY TRACK RELATIVE(12)", .info_data_direction = SCST_DATA_NONE, @@ -1440,7 +1463,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_WRITE_MEDIUM, .info_lba_off = 2, .info_lba_len = 4, .info_len_off = 6, .info_len_len = 4, - .get_cdb_info = get_cdb_info_lba_4_len_4}, + .get_cdb_info = get_cdb_info_lba_4_len_4_wrprotect}, {.ops = 0xAA, .devkey = " O ", .info_op_name = "SEND MESSAGE(12)", .info_data_direction = SCST_DATA_WRITE, @@ -1469,7 +1492,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = { .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, .info_lba_off = 2, .info_lba_len = 4, .info_len_off = 6, .info_len_len = 4, - .get_cdb_info = get_cdb_info_lba_4_len_4}, + .get_cdb_info = get_cdb_info_lba_4_len_4_wrprotect}, {.ops = 0xAF, .devkey = "O OO O ", .info_op_name = "VERIFY(12)", .info_data_direction = SCST_DATA_UNKNOWN, @@ -3206,24 +3229,39 @@ static void scst_adjust_sg(struct scst_cmd *cmd, bool reg_sg, { struct scatterlist *sg; int *sg_cnt; + bool adjust_dif; TRACE_ENTRY(); - EXTRACHECKS_BUG_ON(cmd->sg_buff_modified); + EXTRACHECKS_BUG_ON(cmd->sg_buff_modified || cmd->dif_sg_buff_modified); if (reg_sg) { sg = cmd->sg; sg_cnt = &cmd->sg_cnt; + adjust_dif = (cmd->dif_sg != NULL); } else { sg = *cmd->write_sg; sg_cnt = cmd->write_sg_cnt; + if (sg == cmd->sg) + adjust_dif = (cmd->dif_sg != NULL); + else + adjust_dif = false; } - TRACE_DBG("reg_sg %d, adjust_len %d", reg_sg, adjust_len); + TRACE_DBG("reg_sg %d, adjust_len %d, adjust_dif %d", reg_sg, + adjust_len, adjust_dif); - cmd->sg_buff_modified = __scst_adjust_sg(cmd, sg, sg_cnt, adjust_len, + cmd->sg_buff_modified = !!__scst_adjust_sg(cmd, sg, sg_cnt, adjust_len, &cmd->orig_sg); + if (adjust_dif) { + adjust_len >>= (cmd->dev->block_shift - SCST_DIF_TAG_SHIFT); + TRACE_DBG("DIF adjust_len %d", adjust_len); + cmd->dif_sg_buff_modified = __scst_adjust_sg(cmd, cmd->dif_sg, + &cmd->dif_sg_cnt, adjust_len, + &cmd->orig_dif_sg); + } + TRACE_EXIT(); return; } @@ -3235,13 +3273,16 @@ static void scst_adjust_sg(struct scst_cmd *cmd, bool reg_sg, */ void scst_restore_sg_buff(struct scst_cmd *cmd) { - TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_MEMORY, "cmd %p, sg %p, " + TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_MEMORY, "cmd %p, sg %p, DATA: " "orig_sg_entry %p, orig_entry_offs %d, orig_entry_len %d, " - "orig_sg_cnt %d", cmd, cmd->sg, cmd->orig_sg.orig_sg_entry, - cmd->orig_sg.orig_entry_offs, cmd->orig_sg.orig_entry_len, - cmd->orig_sg.orig_sg_cnt); + "orig_sg_cnt %d, DIF: orig_sg_entry %p, " + "orig_entry_offs %d, orig_entry_len %d, orig_sg_cnt %d", cmd, + cmd->sg, cmd->orig_sg.orig_sg_entry, cmd->orig_sg.orig_entry_offs, + cmd->orig_sg.orig_entry_len, cmd->orig_sg.orig_sg_cnt, + cmd->orig_dif_sg.orig_sg_entry, cmd->orig_dif_sg.orig_entry_offs, + cmd->orig_dif_sg.orig_entry_len, cmd->orig_dif_sg.orig_sg_cnt); - EXTRACHECKS_BUG_ON(!cmd->sg_buff_modified); + EXTRACHECKS_BUG_ON(!(cmd->sg_buff_modified || cmd->dif_sg_buff_modified)); if (cmd->sg_buff_modified) { cmd->orig_sg.orig_sg_entry->offset = cmd->orig_sg.orig_entry_offs; @@ -3249,7 +3290,14 @@ void scst_restore_sg_buff(struct scst_cmd *cmd) *cmd->orig_sg.p_orig_sg_cnt = cmd->orig_sg.orig_sg_cnt; } + if (cmd->dif_sg_buff_modified) { + cmd->orig_dif_sg.orig_sg_entry->offset = cmd->orig_dif_sg.orig_entry_offs; + cmd->orig_dif_sg.orig_sg_entry->length = cmd->orig_dif_sg.orig_entry_len; + *cmd->orig_dif_sg.p_orig_sg_cnt = cmd->orig_dif_sg.orig_sg_cnt; + } + cmd->sg_buff_modified = 0; + cmd->dif_sg_buff_modified = 0; } EXPORT_SYMBOL(scst_restore_sg_buff); @@ -3310,6 +3358,71 @@ void scst_limit_sg_write_len(struct scst_cmd *cmd) return; } +static int scst_full_len_to_data_len(int full_len, int block_shift) +{ + int rem, res; + + res = full_len << block_shift; + rem = do_div(res, (1 << block_shift) + (1 << SCST_DIF_TAG_SHIFT)); + if (unlikely(rem != 0)) + TRACE(TRACE_MINOR, "Reminder %d for full len! (full len%d)", + rem, full_len); + + TRACE_DBG("data len %d (full %d)", res, full_len); + + return res; +} + +/* + * Returns data expected transfer length, i.e. expected transfer length, + * adjusted on DIF tags expected transfer length, if any. + * + * Very expensive, don't call on fast path! + */ +int scst_cmd_get_expected_transfer_len_data(struct scst_cmd *cmd) +{ + int rem, res; + + if (!cmd->tgt_dif_data_expected) + return cmd->expected_transfer_len_full; + + res = cmd->expected_transfer_len_full << cmd->dev->block_shift; + rem = do_div(res, cmd->dev->block_size + (1 << SCST_DIF_TAG_SHIFT)); + if (unlikely(rem != 0)) + TRACE(TRACE_MINOR, "Reminder %d for expected transfer len " + "data! (cmd %p, op %s, expected len full %d)", rem, + cmd, scst_get_opcode_name(cmd), + cmd->expected_transfer_len_full); + + TRACE_DBG("Expected transfer len data %d (cmd %p)", res, cmd); + + return res; +} + +/* + * Returns DIF tags expected transfer length. + * + * Very expensive, don't call on fast path! + */ +int scst_cmd_get_expected_transfer_len_dif(struct scst_cmd *cmd) +{ + int rem, res; + + if (!cmd->tgt_dif_data_expected) + return 0; + + res = cmd->expected_transfer_len_full << SCST_DIF_TAG_SHIFT; + rem = do_div(res, cmd->dev->block_size + (1 << SCST_DIF_TAG_SHIFT)); + if (unlikely(rem != 0)) + TRACE(TRACE_MINOR, "Reminder %d for expected transfer len dif! " + "(cmd %p, op %s, expected len full %d)", rem, cmd, + scst_get_opcode_name(cmd), cmd->expected_transfer_len_full); + + TRACE_DBG("Expected transfer len DIF %d (cmd %p)", res, cmd); + + return res; +} + void scst_adjust_resp_data_len(struct scst_cmd *cmd) { TRACE_ENTRY(); @@ -3320,7 +3433,7 @@ void scst_adjust_resp_data_len(struct scst_cmd *cmd) } cmd->adjusted_resp_data_len = min(cmd->resp_data_len, - cmd->expected_transfer_len); + scst_cmd_get_expected_transfer_len_data(cmd)); if (cmd->adjusted_resp_data_len != cmd->resp_data_len) { TRACE_MEM("Adjusting resp_data_len to %d (cmd %p, sg %p, " @@ -3365,7 +3478,10 @@ void scst_cmd_set_write_not_received_data_len(struct scst_cmd *cmd, if (cmd->write_len == cmd->out_bufflen) goto out; } else if (cmd->expected_data_direction & SCST_DATA_WRITE) { - cmd->write_len = cmd->expected_transfer_len - not_received; + cmd->write_len = cmd->expected_transfer_len_full - not_received; + if (cmd->tgt_dif_data_expected) + cmd->write_len = scst_full_len_to_data_len(cmd->write_len, + cmd->dev->block_shift); if (cmd->write_len == cmd->bufflen) goto out; } @@ -3400,7 +3516,7 @@ void scst_cmd_set_write_no_data_received(struct scst_cmd *cmd) (cmd->expected_data_direction & SCST_DATA_WRITE)) w = cmd->expected_out_transfer_len; else - w = cmd->expected_transfer_len; + w = cmd->expected_transfer_len_full; scst_cmd_set_write_not_received_data_len(cmd, w); @@ -3437,7 +3553,10 @@ bool __scst_get_resid(struct scst_cmd *cmd, int *resid, int *bidi_out_resid) } if (cmd->expected_data_direction & SCST_DATA_READ) { - *resid = cmd->expected_transfer_len - cmd->resp_data_len; + int resp = cmd->resp_data_len; + if (cmd->tgt_dif_data_expected) + resp += (resp >> cmd->dev->block_shift) << SCST_DIF_TAG_SHIFT; + *resid = cmd->expected_transfer_len_full - resp; if ((cmd->expected_data_direction & SCST_DATA_WRITE) && bidi_out_resid) { if (cmd->write_len < cmd->expected_out_transfer_len) *bidi_out_resid = cmd->expected_out_transfer_len - @@ -3446,18 +3565,34 @@ bool __scst_get_resid(struct scst_cmd *cmd, int *resid, int *bidi_out_resid) *bidi_out_resid = cmd->write_len - cmd->out_bufflen; } } else if (cmd->expected_data_direction & SCST_DATA_WRITE) { - if (cmd->write_len < cmd->expected_transfer_len) - *resid = cmd->expected_transfer_len - cmd->write_len; - else + int wl = cmd->write_len; + if (cmd->tgt_dif_data_expected) + wl += (wl >> cmd->dev->block_shift) << SCST_DIF_TAG_SHIFT; + if (wl < cmd->expected_transfer_len_full) + *resid = cmd->expected_transfer_len_full - wl; + else { *resid = cmd->write_len - cmd->bufflen; + if (cmd->tgt_dif_data_expected) { + int r = *resid; + if (r < 0) + r = -r; + r += (r >> cmd->dev->block_shift) << SCST_DIF_TAG_SHIFT; + if (*resid > 0) + *resid = r; + else + *resid = -r; + } + } } res = true; TRACE_DBG("cmd %p, resid %d, bidi_out_resid %d (resp_data_len %d, " - "expected_data_direction %d, write_len %d, bufflen %d)", cmd, - *resid, bidi_out_resid ? *bidi_out_resid : 0, cmd->resp_data_len, - cmd->expected_data_direction, cmd->write_len, cmd->bufflen); + "expected_data_direction %d, write_len %d, bufflen %d, " + "tgt_dif_data_expected %d)", cmd, *resid, + bidi_out_resid ? *bidi_out_resid : 0, cmd->resp_data_len, + cmd->expected_data_direction, cmd->write_len, cmd->bufflen, + cmd->tgt_dif_data_expected); out: TRACE_EXIT_RES(res); @@ -3721,11 +3856,27 @@ int scst_alloc_tgt(struct scst_tgt_template *tgtt, struct scst_tgt **tgt) init_waitqueue_head(&t->unreg_waitQ); t->tgtt = tgtt; t->sg_tablesize = tgtt->sg_tablesize; + t->tgt_dif_supported = tgtt->dif_supported; + t->tgt_hw_dif_type1_supported = tgtt->hw_dif_type1_supported; + t->tgt_hw_dif_type2_supported = tgtt->hw_dif_type2_supported; + t->tgt_hw_dif_type3_supported = tgtt->hw_dif_type3_supported; + t->tgt_hw_dif_ip_supported = tgtt->hw_dif_ip_supported; + t->tgt_hw_dif_same_sg_layout_required = tgtt->hw_dif_same_sg_layout_required; + t->tgt_supported_dif_block_sizes = tgtt->supported_dif_block_sizes; spin_lock_init(&t->tgt_lock); INIT_LIST_HEAD(&t->retry_cmd_list); init_timer(&t->retry_timer); t->retry_timer.data = (unsigned long)t; t->retry_timer.function = scst_tgt_retry_timer_fn; + atomic_set(&t->tgt_dif_app_failed_tgt, 0); + atomic_set(&t->tgt_dif_ref_failed_tgt, 0); + atomic_set(&t->tgt_dif_guard_failed_tgt, 0); + atomic_set(&t->tgt_dif_app_failed_scst, 0); + atomic_set(&t->tgt_dif_ref_failed_scst, 0); + atomic_set(&t->tgt_dif_guard_failed_scst, 0); + atomic_set(&t->tgt_dif_app_failed_dev, 0); + atomic_set(&t->tgt_dif_ref_failed_dev, 0); + atomic_set(&t->tgt_dif_guard_failed_dev, 0); #ifdef CONFIG_SCST_PROC res = gen_relative_target_port_id(&t->rel_tgt_id); @@ -3776,6 +3927,13 @@ static void scst_init_order_data(struct scst_order_data *order_data) return; } +static int scst_dif_none(struct scst_cmd *cmd); +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS +static int scst_dif_none_type1(struct scst_cmd *cmd); +#else +#define scst_dif_none_type1 scst_dif_none +#endif + /* Called under scst_mutex and suspended activity */ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev) { @@ -3811,6 +3969,11 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev) dev->pr_type = TYPE_UNSPECIFIED; INIT_LIST_HEAD(&dev->dev_registrants_list); + BUILD_BUG_ON(SCST_DIF_NO_CHECK_APP_TAG != 0); + dev->dev_dif_static_app_tag = SCST_DIF_NO_CHECK_APP_TAG; + dev->dev_dif_static_app_ref_tag = SCST_DIF_NO_CHECK_APP_TAG; + dev->dev_dif_fn = scst_dif_none; + scst_init_order_data(&dev->dev_order_data); scst_init_threads(&dev->dev_cmd_threads); @@ -3927,12 +4090,82 @@ static void scst_del_free_acg_dev(struct scst_acg_dev *acg_dev, bool del_sysfs) return; } +static int scst_check_dif_compatibility(const struct scst_acg *acg, + const struct scst_device *dev) +{ + int res = -EINVAL; + struct scst_tgt *tgt; + const int *p; + bool supported; + + TRACE_ENTRY(); + + if (dev->dev_dif_mode == SCST_DIF_MODE_NONE) + goto out_ok; + + tgt = acg->tgt; + + if (!tgt->tgt_dif_supported) { + PRINT_ERROR("Target %s doesn't support T10-PI (device %s)", + tgt->tgt_name, dev->virt_name); + goto out; + } + + if (dev->dev_dif_mode & SCST_DIF_MODE_TGT) { + if ((dev->dev_dif_type == 1) && !tgt->tgt_hw_dif_type1_supported) { + PRINT_ERROR("Target %s doesn't support type 1 " + "protection TGT mode (device %s)", tgt->tgt_name, + dev->virt_name); + goto out; + } + + if ((dev->dev_dif_type == 2) && !tgt->tgt_hw_dif_type2_supported) { + PRINT_ERROR("Target %s doesn't support type 2 " + "protection TGT mode (device %s)", tgt->tgt_name, + dev->virt_name); + goto out; + } + + if ((dev->dev_dif_type == 3) && !tgt->tgt_hw_dif_type3_supported) { + PRINT_ERROR("Target %s doesn't support type 3 " + "protection TGT mode (device %s)", tgt->tgt_name, + dev->virt_name); + goto out; + } + } + + if (tgt->tgt_supported_dif_block_sizes == NULL) + goto out_ok; + + p = tgt->tgt_supported_dif_block_sizes; + supported = false; + while (*p != 0) { + if (*p == dev->block_size) { + supported = true; + break; + } + p++; + } + if (!supported) { + PRINT_ERROR("Target %s doesn't support block size %d of " + "device %s", tgt->tgt_name, dev->block_size, dev->virt_name); + goto out; + } + +out_ok: + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + /* The activity supposed to be suspended and scst_mutex held */ int scst_acg_add_lun(struct scst_acg *acg, struct kobject *parent, struct scst_device *dev, uint64_t lun, int read_only, bool gen_scst_report_luns_changed, struct scst_acg_dev **out_acg_dev) { - int res = 0; + int res; struct scst_acg_dev *acg_dev; struct scst_tgt_dev *tgt_dev; struct scst_session *sess; @@ -3942,12 +4175,24 @@ int scst_acg_add_lun(struct scst_acg *acg, struct kobject *parent, INIT_LIST_HEAD(&tmp_tgt_dev_list); + res = scst_check_dif_compatibility(acg, dev); + if (res != 0) + goto out; + acg_dev = scst_alloc_acg_dev(acg, dev, lun); if (acg_dev == NULL) { res = -ENOMEM; goto out; } acg_dev->acg_dev_rd_only = read_only; + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) { + /* Devices are allowed to store only CRCs */ + acg_dev->acg_dev_dif_guard_format = SCST_DIF_GUARD_FORMAT_CRC; + } else + acg_dev->acg_dev_dif_guard_format = + acg->tgt->tgt_hw_dif_ip_supported && !dev->dev_dif_ip_not_supported ? + SCST_DIF_GUARD_FORMAT_IP : + SCST_DIF_GUARD_FORMAT_CRC; TRACE_DBG("Adding acg_dev %p to acg_dev_list and dev_acg_dev_list", acg_dev); @@ -4584,6 +4829,9 @@ out_deinit: return; } +static __be16 scst_dif_crc_fn(const void *data, unsigned int len); +static __be16 scst_dif_ip_fn(const void *data, unsigned int len); + /* * scst_mutex supposed to be held, there must not be parallel activity in this * session. May be invoked from inside scst_check_reassign_sessions() which @@ -4614,6 +4862,28 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev->lun = acg_dev->lun; tgt_dev->acg_dev = acg_dev; tgt_dev->tgt_dev_rd_only = acg_dev->acg_dev_rd_only || dev->dev_rd_only; + if (sess->tgt->tgt_forwarding) + set_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags); + else + clear_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags); + tgt_dev->hw_dif_same_sg_layout_required = sess->tgt->tgt_hw_dif_same_sg_layout_required; + tgt_dev->tgt_dev_dif_guard_format = acg_dev->acg_dev_dif_guard_format; + if (tgt_dev->tgt_dev_dif_guard_format == SCST_DIF_GUARD_FORMAT_IP) + tgt_dev->tgt_dev_dif_crc_fn = scst_dif_ip_fn; + else { + EXTRACHECKS_BUG_ON(tgt_dev->tgt_dev_dif_guard_format != SCST_DIF_GUARD_FORMAT_CRC); + tgt_dev->tgt_dev_dif_crc_fn = scst_dif_crc_fn; + } + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_dev, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_dev, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_dev, 0); + tgt_dev->sess = sess; atomic_set(&tgt_dev->tgt_dev_cmd_count, 0); if (acg_dev->acg->acg_black_hole_type != SCST_ACG_BLACK_HOLE_NONE) @@ -4636,7 +4906,9 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev->max_sg_cnt = min(ini_sg, sess->tgt->sg_tablesize); if ((sess->tgt->tgtt->use_clustering || ini_use_clustering) && - !sess->tgt->tgtt->no_clustering) + !sess->tgt->tgtt->no_clustering && + !(sess->tgt->tgt_hw_dif_same_sg_layout_required && + (tgt_dev->dev->dev_dif_type != 0))) scst_sgv_pool_use_norm_clust(tgt_dev); if (sess->tgt->tgtt->unchecked_isa_dma || ini_unchecked_isa_dma) @@ -5052,6 +5324,24 @@ out: return res; } +static void scst_prelim_finish_internal_cmd(struct scst_cmd *cmd) +{ + unsigned long flags; + + TRACE_ENTRY(); + + sBUG_ON(!cmd->internal); + + spin_lock_irqsave(&cmd->sess->sess_list_lock, flags); + list_del(&cmd->sess_cmd_list_entry); + spin_unlock_irqrestore(&cmd->sess->sess_list_lock, flags); + + __scst_cmd_put(cmd); + + TRACE_EXIT(); + return; +} + int scst_prepare_request_sense(struct scst_cmd *orig_cmd) { int res = 0; @@ -5079,7 +5369,7 @@ int scst_prepare_request_sense(struct scst_cmd *orig_cmd) rs_cmd->cdb[1] |= scst_get_cmd_dev_d_sense(orig_cmd); rs_cmd->expected_data_direction = SCST_DATA_READ; - rs_cmd->expected_transfer_len = SCST_SENSE_BUFFERSIZE; + rs_cmd->expected_transfer_len_full = SCST_SENSE_BUFFERSIZE; rs_cmd->expected_values_set = 1; TRACE_MGMT_DBG("Adding REQUEST SENSE cmd %p to head of active " @@ -5158,6 +5448,12 @@ struct scst_write_same_priv { int64_t ws_cur_lba; /* in blocks */ int ws_left_to_send; /* in blocks */ + __be16 guard_tag; + __be16 app_tag; + uint32_t ref_tag; + unsigned int dif_valid:1; + unsigned int inc_ref_tag:1; + int ws_max_each;/* in blocks */ int ws_cur_in_flight; @@ -5165,20 +5461,37 @@ struct scst_write_same_priv { struct scatterlist *ws_sg; }; +#ifdef CONFIG_SCST_EXTRACHECKS +static u64 sg_data_length(struct scatterlist *sgl, int nr) +{ + struct scatterlist *sg; + u64 len = 0; + int i; + + for_each_sg(sgl, sg, nr, i) + len += sg->length; + + return len; +} +#endif + /* ws_mutex suppose to be locked */ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, int64_t lba, int blocks) { struct scst_cmd *ws_cmd = wsp->ws_orig_cmd; + struct scst_device *dev = ws_cmd->dev; struct scatterlist *ws_sg = wsp->ws_sg; int res; - uint8_t write16_cdb[16]; + uint8_t write_cdb[32]; + int write_cdb_len; int len = blocks << ws_cmd->dev->block_shift; struct scst_cmd *cmd; + bool needs_dif = wsp->dif_valid; TRACE_ENTRY(); - EXTRACHECKS_BUG_ON(blocks > wsp->ws_sg_cnt); + EXTRACHECKS_BUG_ON(len != sg_data_length(wsp->ws_sg, wsp->ws_sg_cnt)); if (unlikely(test_bit(SCST_CMD_ABORTED, &ws_cmd->cmd_flags)) || unlikely(ws_cmd->completed)) { @@ -5189,26 +5502,100 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, goto out; } - memset(write16_cdb, 0, sizeof(write16_cdb)); - write16_cdb[0] = WRITE_16; - put_unaligned_be64(lba, &write16_cdb[2]); - put_unaligned_be32(blocks, &write16_cdb[10]); + if (!needs_dif || (dev->dev_dif_type != 2)) { + memset(write_cdb, 0, sizeof(write_cdb)); + write_cdb[0] = WRITE_16; + write_cdb_len = 16; + write_cdb[1] = ws_cmd->cdb[1]; + if (needs_dif) { + uint8_t wrprotect = ws_cmd->cdb[1] & 0xE0; + if (wrprotect == 0) + wrprotect = 0x20; + write_cdb[1] |= wrprotect; + } + put_unaligned_be64(lba, &write_cdb[2]); + put_unaligned_be32(blocks, &write_cdb[10]); + } else { + /* There might be WRITE SAME(16) with WRPROTECT 0 here */ + uint8_t wrprotect; + if (ws_cmd->cdb_len != 32) + wrprotect = ws_cmd->cdb[1] & 0xE0; + else + wrprotect = ws_cmd->cdb[10] & 0xE0; + if (wrprotect == 0) + wrprotect = 0x20; + write_cdb[0] = VARIABLE_LENGTH_CMD; + put_unaligned_be16(SUBCODE_WRITE_32, &write_cdb[8]); + write_cdb[7] = 0x18; + write_cdb_len = 32; + write_cdb[10] = ws_cmd->cdb[10]; + write_cdb[10] |= wrprotect; + put_unaligned_be64(lba, &write_cdb[12]); + put_unaligned_be32(blocks, &write_cdb[28]); + put_unaligned_be32(wsp->ref_tag, &write_cdb[20]); + put_unaligned(wsp->app_tag, &write_cdb[24]); + write_cdb[26] = ws_cmd->cdb[26]; + write_cdb[27] = ws_cmd->cdb[27]; + } - cmd = scst_create_prepare_internal_cmd(ws_cmd, write16_cdb, - sizeof(write16_cdb), SCST_CMD_QUEUE_SIMPLE); + cmd = scst_create_prepare_internal_cmd(ws_cmd, write_cdb, + write_cdb_len, SCST_CMD_QUEUE_SIMPLE); if (cmd == NULL) { res = -ENOMEM; goto out_busy; } cmd->expected_data_direction = SCST_DATA_WRITE; - cmd->expected_transfer_len = len; + cmd->expected_transfer_len_full = len; cmd->expected_values_set = 1; cmd->tgt_i_priv = wsp; + if (needs_dif) { + struct scatterlist *dif_sg, *s; + struct sgv_pool_obj *dif_sgv = NULL; + int dif_bufflen, i, dif_sg_cnt = 0; + + TRACE_DBG("Allocating and filling DIF buff for cmd %p " + "(ws_cmd %p)", cmd, ws_cmd); + + dif_bufflen = blocks << SCST_DIF_TAG_SHIFT; + cmd->expected_transfer_len_full += dif_bufflen; + + dif_sg = sgv_pool_alloc(ws_cmd->tgt_dev->pool, + dif_bufflen, GFP_KERNEL, 0, &dif_sg_cnt, &dif_sgv, + &cmd->dev->dev_mem_lim, NULL); + if (unlikely(dif_sg == NULL)) { + TRACE(TRACE_OUT_OF_MEM, "Unable to alloc DIF sg " + "for %d blocks", blocks); + res = -ENOMEM; + goto out_free_cmd; + } + cmd->out_sgv = dif_sgv; /* hacky, but it isn't used for WRITE(16/32) */ + cmd->tgt_i_dif_sg = dif_sg; + cmd->tgt_i_dif_sg_cnt = dif_sg_cnt; + + TRACE_DBG("dif_sg %p, cnt %d", dif_sg, dif_sg_cnt); + + for_each_sg(dif_sg, s, dif_sg_cnt, i) { + int left = (s->length - s->offset) >> SCST_DIF_TAG_SHIFT; + struct t10_pi_tuple *t = sg_virt(s); + TRACE_DBG("sg %p, offset %d, length %d, left %d", s, + s->offset, s->length, left); + while (left > 0) { + t->app_tag = wsp->app_tag; + t->ref_tag = cpu_to_be32(wsp->ref_tag); + if (wsp->inc_ref_tag) + wsp->ref_tag++; + t->guard_tag = wsp->guard_tag; + t++; + left--; + } + } + } + cmd->tgt_i_sg = ws_sg; - cmd->tgt_i_sg_cnt = blocks; + cmd->tgt_i_sg_cnt = wsp->ws_sg_cnt; cmd->tgt_i_data_buf_alloced = 1; wsp->ws_cur_lba += blocks; @@ -5226,6 +5613,9 @@ out: TRACE_EXIT_RES(res); return res; +out_free_cmd: + scst_prelim_finish_internal_cmd(cmd); + out_busy: scst_set_busy(ws_cmd); goto out; @@ -5241,6 +5631,8 @@ static void scst_ws_finished(struct scst_write_same_priv *wsp) sBUG_ON(wsp->ws_cur_in_flight != 0); + if (sg_page(&wsp->ws_sg[0]) != sg_page(ws_cmd->sg)) + __free_page(sg_page(&wsp->ws_sg[0])); kfree(wsp->ws_sg); kfree(wsp); @@ -5251,6 +5643,51 @@ static void scst_ws_finished(struct scst_write_same_priv *wsp) return; } +/* + * If there is a tail with fewer segments than ws_max_each, adjust the SG + * vector and submit a WRITE command for the tail after all other in-flight + * commands have finished. + */ +static void scst_ws_process_tail(struct scst_write_same_priv *wsp) +{ + struct scst_cmd *ws_cmd = wsp->ws_orig_cmd; + struct scatterlist *sg; + unsigned left; + int i; + + TRACE_ENTRY(); + + lockdep_assert_held(&wsp->ws_mutex); + EXTRACHECKS_BUG_ON(wsp->ws_cur_in_flight > 0); + EXTRACHECKS_BUG_ON(wsp->ws_left_to_send >= wsp->ws_max_each); + + wsp->ws_max_each = wsp->ws_left_to_send; + left = wsp->ws_left_to_send << ws_cmd->dev->block_shift; + for_each_sg(wsp->ws_sg, sg, wsp->ws_sg_cnt, i) { + u32 len = min(left, sg->length); + + if (sg->length > len) { + TRACE_DBG("Processing WS tail of %d << %d = %d bytes - adjusted length of element %d from %d to %d", + wsp->ws_left_to_send, + ws_cmd->dev->block_shift, + wsp->ws_left_to_send << ws_cmd->dev->block_shift, + i, sg->length, len); + sg->length = len; + sg_mark_end(sg); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) + /* Old versions of sg_mark_end() clear page_link. */ + BUG_ON(sg_page(sg) == NULL); +#endif + wsp->ws_sg_cnt = i + 1; + break; + } + left -= len; + } + + TRACE_EXIT(); + return; +} + /* Must be called in a thread context and no locks */ static void scst_ws_write_cmd_finished(struct scst_cmd *cmd) { @@ -5263,6 +5700,9 @@ static void scst_ws_write_cmd_finished(struct scst_cmd *cmd) TRACE_DBG("Write cmd %p finished (ws cmd %p, ws_cur_in_flight %d)", cmd, ws_cmd, wsp->ws_cur_in_flight); + if (cmd->out_sgv != NULL) + sgv_pool_free(cmd->out_sgv, &cmd->dev->dev_mem_lim); + cmd->sg = NULL; cmd->sg_cnt = 0; @@ -5292,6 +5732,13 @@ static void scst_ws_write_cmd_finished(struct scst_cmd *cmd) if (wsp->ws_left_to_send == 0) goto out_check_finish; + if (wsp->ws_left_to_send < wsp->ws_max_each) { + if (wsp->ws_cur_in_flight > 0) + goto out_check_finish; + else + scst_ws_process_tail(wsp); + } + blocks = min_t(int, wsp->ws_left_to_send, wsp->ws_max_each); rc = scst_ws_push_single_write(wsp, wsp->ws_cur_lba, blocks); @@ -5326,13 +5773,15 @@ static void scst_ws_gen_writes(struct scst_write_same_priv *wsp) mutex_lock(&wsp->ws_mutex); - while ((wsp->ws_left_to_send > 0) && - (wsp->ws_cur_in_flight < SCST_MAX_IN_FLIGHT_INTERNAL_COMMANDS)) { - int rc, blocks; + if (wsp->ws_left_to_send < wsp->ws_max_each) + scst_ws_process_tail(wsp); - blocks = min_t(int, wsp->ws_left_to_send, wsp->ws_max_each); + while (wsp->ws_left_to_send >= wsp->ws_max_each && + wsp->ws_cur_in_flight < SCST_MAX_IN_FLIGHT_INTERNAL_COMMANDS) { + int rc; - rc = scst_ws_push_single_write(wsp, wsp->ws_cur_lba, blocks); + rc = scst_ws_push_single_write(wsp, wsp->ws_cur_lba, + wsp->ws_max_each); if (rc != 0) goto out_err; @@ -5366,7 +5815,11 @@ out_err: void scst_write_same(struct scst_cmd *cmd) { struct scst_write_same_priv *wsp; - int i; + int i, rc; + struct page *pg = NULL; + struct scatterlist *sg; + unsigned int offset, length, mult, ws_sg_blocks, left; + uint8_t ctrl_offs = (cmd->cdb_len < 32) ? 1 : 10; TRACE_ENTRY(); @@ -5382,10 +5835,10 @@ void scst_write_same(struct scst_cmd *cmd) goto out_done; } - if (unlikely((cmd->cdb[1] & 0x6) != 0)) { + if (unlikely((cmd->cdb[ctrl_offs] & 0x6) != 0)) { TRACE(TRACE_MINOR, "LBDATA and/or PBDATA (ctrl %x) are not " - "supported", cmd->cdb[1]); - scst_set_invalid_field_in_cdb(cmd, 1, + "supported", cmd->cdb[ctrl_offs]); + scst_set_invalid_field_in_cdb(cmd, ctrl_offs, SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); goto out_done; } @@ -5398,6 +5851,10 @@ void scst_write_same(struct scst_cmd *cmd) goto out_done; } + rc = scst_dif_process_write(cmd); + if (unlikely(rc != 0)) + goto out_done; + wsp = kzalloc(sizeof(*wsp), GFP_KERNEL); if (wsp == NULL) { PRINT_ERROR("Unable to allocate ws_priv (size %zd, cmd %p)", @@ -5413,17 +5870,71 @@ void scst_write_same(struct scst_cmd *cmd) wsp->ws_left_to_send = cmd->data_len >> cmd->dev->block_shift; wsp->ws_max_each = SCST_MAX_EACH_INTERNAL_IO_SIZE >> cmd->dev->block_shift; - wsp->ws_sg_cnt = min_t(int, wsp->ws_left_to_send, wsp->ws_max_each); + if (cmd->bufflen <= PAGE_SIZE / 2) + pg = alloc_page(GFP_KERNEL); + if (pg) { + void *src, *dst; + int k; + + mult = 0; + src = kmap(sg_page(cmd->sg)); + dst = kmap(pg); + for (k = 0; k < PAGE_SIZE; k += cmd->bufflen, mult++) + memcpy(dst + k, src + cmd->sg->offset, cmd->bufflen); + kunmap(pg); + kunmap(src); + offset = 0; + length = k; + } else { + pg = sg_page(cmd->sg); + offset = cmd->sg->offset; + length = cmd->sg->length; + mult = 1; + } + + ws_sg_blocks = min_t(int, wsp->ws_left_to_send, wsp->ws_max_each); + wsp->ws_sg_cnt = (ws_sg_blocks + mult - 1) / mult; wsp->ws_sg = kmalloc(wsp->ws_sg_cnt * sizeof(*wsp->ws_sg), GFP_KERNEL); if (wsp->ws_sg == NULL) { PRINT_ERROR("Unable to alloc sg for %d entries", wsp->ws_sg_cnt); goto out_free; } sg_init_table(wsp->ws_sg, wsp->ws_sg_cnt); + left = ws_sg_blocks << cmd->dev->block_shift; + for_each_sg(wsp->ws_sg, sg, wsp->ws_sg_cnt, i) { + u32 len = min(left, length); - for (i = 0; i < wsp->ws_sg_cnt; i++) { - sg_set_page(&wsp->ws_sg[i], sg_page(cmd->sg), - cmd->sg->length, cmd->sg->offset); + sg_set_page(sg, pg, len, offset); + left -= len; + } + sBUG_ON(left != 0); /* crash here to avoid data corruption */ + + if (scst_cmd_needs_dif_buf(cmd)) { + struct t10_pi_tuple *t; + struct scatterlist *tags_sg = NULL; + int tags_len = 0; + + t = (struct t10_pi_tuple *)scst_get_dif_buf(cmd, &tags_sg, &tags_len); + + wsp->app_tag = t->app_tag; + wsp->ref_tag = be32_to_cpu(t->ref_tag); + wsp->guard_tag = t->guard_tag; + wsp->dif_valid = 1; + + switch (cmd->dev->dev_dif_type) { + case 1: + case 2: + wsp->inc_ref_tag = 1; + break; + case 3: + wsp->inc_ref_tag = 0; + break; + default: + sBUG_ON(1); + break; + } + + scst_put_dif_buf(cmd, t); } scst_ws_gen_writes(wsp); @@ -5433,6 +5944,8 @@ out: return; out_free: + if (pg && pg != sg_page(cmd->sg)) + __free_page(pg); kfree(wsp); out_busy: @@ -6121,6 +6634,29 @@ failed: goto out; } +static void scst_restore_dif_sg(struct scst_cmd *cmd) +{ + int left = cmd->bufflen; + struct scatterlist *sg = cmd->dif_sg; + + TRACE_ENTRY(); + + if (!cmd->dif_sg_normalized) + goto out; + + do { + sg->length = min_t(int, PAGE_SIZE, left); + left -= sg->length; + TRACE_DBG("DIF sg %p, restored len %d (left %d)", sg, + sg->length, left); + sg = __sg_next_inline(sg); + } while (left > 0); + +out: + TRACE_EXIT(); + return; +} + int scst_alloc_space(struct scst_cmd *cmd) { gfp_t gfp_mask; @@ -6146,6 +6682,42 @@ int scst_alloc_space(struct scst_cmd *cmd) if (!scst_on_sg_tablesize_low(cmd, false)) goto out_sg_free; + if ((scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE) || + (scst_get_dif_action(scst_get_dev_dif_actions(cmd->cmd_dif_actions)) != SCST_DIF_ACTION_NONE)) { + int dif_bufflen; + bool same_layout; + + same_layout = tgt_dev->hw_dif_same_sg_layout_required; + if (!same_layout) + dif_bufflen = __scst_cmd_get_bufflen_dif(cmd); + else + dif_bufflen = cmd->bufflen; + + cmd->dif_sg = sgv_pool_alloc(tgt_dev->pool, dif_bufflen, gfp_mask, flags, + &cmd->dif_sg_cnt, &cmd->dif_sgv, &cmd->dev->dev_mem_lim, NULL); + if (unlikely(cmd->dif_sg == NULL)) + goto out_sg_free; + + if (same_layout) { + int left = dif_bufflen; + struct scatterlist *sgd = cmd->sg; + struct scatterlist *sgt = cmd->dif_sg; + int block_shift = cmd->dev->block_shift; + + EXTRACHECKS_BUG_ON(left != cmd->bufflen); + + do { + left -= sgt->length; + sgt->length = (sgd->length >> block_shift) << SCST_DIF_TAG_SHIFT; + TRACE_SG("sgt %p, new len %d", sgt, sgt->length); + sgd = __sg_next_inline(sgd); + sgt = __sg_next_inline(sgt); + } while (left > 0); + + cmd->dif_sg_normalized = 1; + } + } + if (cmd->data_direction != SCST_DATA_BIDI) goto success; @@ -6153,7 +6725,7 @@ int scst_alloc_space(struct scst_cmd *cmd) flags, &cmd->out_sg_cnt, &cmd->out_sgv, &cmd->dev->dev_mem_lim, NULL); if (unlikely(cmd->out_sg == NULL)) - goto out_sg_free; + goto out_dif_sg_free; if (unlikely(cmd->out_sg_cnt > tgt_dev->max_sg_cnt)) if (!scst_on_sg_tablesize_low(cmd, true)) @@ -6172,6 +6744,15 @@ out_out_sg_free: cmd->out_sg = NULL; cmd->out_sg_cnt = 0; +out_dif_sg_free: + if (cmd->dif_sgv != NULL) { + scst_restore_dif_sg(cmd); + sgv_pool_free(cmd->dif_sgv, &cmd->dev->dev_mem_lim); + cmd->dif_sgv = NULL; + cmd->dif_sg = NULL; + cmd->dif_sg_cnt = 0; + } + out_sg_free: sgv_pool_free(cmd->sgv, &cmd->dev->dev_mem_lim); cmd->sgv = NULL; @@ -6208,12 +6789,20 @@ static void scst_release_space(struct scst_cmd *cmd) cmd->out_bufflen = 0; } + if (cmd->dif_sgv != NULL) { + scst_restore_dif_sg(cmd); + sgv_pool_free(cmd->dif_sgv, &cmd->dev->dev_mem_lim); + } + sgv_pool_free(cmd->sgv, &cmd->dev->dev_mem_lim); out_zero: cmd->sgv = NULL; cmd->sg_cnt = 0; cmd->sg = NULL; + cmd->dif_sgv = NULL; + cmd->dif_sg = NULL; + cmd->dif_sg_cnt = 0; cmd->bufflen = 0; cmd->data_len = 0; @@ -6841,7 +7430,9 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data, rq->timeout = cmd->timeout; rq->retries = cmd->retries; rq->end_io_data = sioc; - rq->cmd_flags |= REQ_QUIET; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + rq->cmd_flags |= REQ_FAILFAST_MASK; +#endif blk_execute_rq_nowait(rq->q, NULL, rq, (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE), scsi_end_async); @@ -6873,13 +7464,15 @@ EXPORT_SYMBOL(scst_scsi_exec_async); /** * scst_copy_sg() - copy data between the command's SGs * - * Copies data between cmd->tgt_i_sg and cmd->sg in direction defined by + * Copies data between cmd->tgt_i_sg and cmd->sg as well as + * cmd->tgt_i_dif_sg and cmd->dif_sg in direction defined by * copy_dir parameter. */ void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir) { struct scatterlist *src_sg, *dst_sg; - unsigned int to_copy; + struct scatterlist *src_sg_dif, *dst_sg_dif; + unsigned int to_copy, to_copy_dif; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) int atomic = scst_cmd_atomic(cmd); #endif @@ -6891,20 +7484,31 @@ void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir) src_sg = cmd->tgt_i_sg; dst_sg = cmd->sg; to_copy = cmd->bufflen; + src_sg_dif = cmd->tgt_i_dif_sg; + dst_sg_dif = cmd->dif_sg; + to_copy_dif = scst_cmd_get_bufflen_dif(cmd); } else { TRACE_MEM("BIDI cmd %p", cmd); src_sg = cmd->tgt_out_sg; dst_sg = cmd->out_sg; to_copy = cmd->out_bufflen; + src_sg_dif = NULL; + dst_sg_dif = NULL; + to_copy_dif = 0; } } else { src_sg = cmd->sg; dst_sg = cmd->tgt_i_sg; to_copy = cmd->adjusted_resp_data_len; + src_sg_dif = cmd->dif_sg; + dst_sg_dif = cmd->tgt_i_dif_sg; + to_copy_dif = scst_cmd_get_bufflen_dif(cmd); } - TRACE_MEM("cmd %p, copy_dir %d, src_sg %p, dst_sg %p, to_copy %lld", - cmd, copy_dir, src_sg, dst_sg, (long long)to_copy); + TRACE_MEM("cmd %p, copy_dir %d, src_sg %p, dst_sg %p, to_copy %lld, " + "src_sg_dif %p, dst_sg_dif %p, to_copy_dif %lld", + cmd, copy_dir, src_sg, dst_sg, (long long)to_copy, + src_sg_dif, dst_sg_dif, (long long)to_copy_dif); if (unlikely(src_sg == NULL) || unlikely(dst_sg == NULL) || unlikely(to_copy == 0)) { @@ -6923,6 +7527,17 @@ void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir) sg_copy(dst_sg, src_sg, 0, to_copy); #endif + if ((src_sg_dif == NULL) || (dst_sg_dif == NULL) || (to_copy_dif == 0)) + goto out; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + sg_copy(dst_sg_dif, src_sg_dif, 0, to_copy_dif, + atomic ? KM_SOFTIRQ0 : KM_USER0, + atomic ? KM_SOFTIRQ1 : KM_USER1); +#else + sg_copy(dst_sg_dif, src_sg_dif, 0, to_copy_dif); +#endif + out: TRACE_EXIT(); return; @@ -7073,10 +7688,1835 @@ out: } EXPORT_SYMBOL(scst_put_buf_full); -static const int SCST_CDB_LENGTH[8] = { 6, 10, 10, 0, 16, 12, 0, 0 }; +static __be16 scst_dif_crc_fn(const void *data, unsigned int len) +{ + return cpu_to_be16(crc_t10dif(data, len)); +} + +static __be16 scst_dif_ip_fn(const void *data, unsigned int len) +{ + return (__force __be16)ip_compute_csum(data, len); +} + +static int scst_verify_dif_type1(struct scst_cmd *cmd) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + enum scst_dif_actions checks = scst_get_dif_checks(cmd->cmd_dif_actions); + int len, tags_len = 0, tag_size = 1 << SCST_DIF_TAG_SHIFT; + struct scatterlist *tags_sg = NULL; + uint8_t *buf, *tags_buf = NULL; + const struct t10_pi_tuple *t = NULL; /* to silence compiler warning */ + uint64_t lba = cmd->lba; + int block_size = dev->block_size, block_shift = dev->block_shift; + __be16 (*crc_fn)(const void *buffer, unsigned int len); + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type != 1); + +#ifdef CONFIG_SCST_EXTRACHECKS + switch (scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions))) { + case SCST_DIF_ACTION_STRIP: + case SCST_DIF_ACTION_PASS_CHECK: + break; + default: + EXTRACHECKS_BUG_ON(1); + break; + } + EXTRACHECKS_BUG_ON(checks == SCST_DIF_ACTION_NONE); +#endif + + crc_fn = cmd->tgt_dev->tgt_dev_dif_crc_fn; + + len = scst_get_buf_first(cmd, &buf); + while (len > 0) { + int i; + uint8_t *cur_buf = buf; + + for (i = 0; i < (len >> block_shift); i++) { + if (tags_buf == NULL) { + tags_buf = scst_get_dif_buf(cmd, &tags_sg, &tags_len); + EXTRACHECKS_BUG_ON(tags_len <= 0); + TRACE_DBG("tags_buf %p", tags_buf); + TRACE_BUFF_FLAG(TRACE_DEBUG, "Tags to verify", + tags_buf, tags_len); + t = (struct t10_pi_tuple *)tags_buf; + } + + if (t->app_tag == SCST_DIF_NO_CHECK_ALL_APP_TAG) { + TRACE_DBG("Skipping tag %lld (cmd %p)", + (long long)lba, cmd); + goto next; + } + + if (checks & SCST_DIF_CHECK_APP_TAG) { + if (t->app_tag != dev->dev_dif_static_app_tag) { + PRINT_WARNING("APP TAG check failed, " + "expected 0x%x, seeing " + "0x%x (cmd %p (op %s), lba %lld, " + "dev %s)", dev->dev_dif_static_app_tag, + t->app_tag, cmd, scst_get_opcode_name(cmd), + (long long)lba, dev->virt_name); + scst_dif_acc_app_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_app_tag_check_failed)); + res = -EIO; + goto out_put; + } + } + + if (checks & SCST_DIF_CHECK_REF_TAG) { + if (t->ref_tag != cpu_to_be32(lba & 0xFFFFFFFF)) { + PRINT_WARNING("REF TAG check failed, " + "expected 0x%x, seeing " + "0x%x (cmd %p (op %s), lba %lld, " + "dev %s)", cpu_to_be32(lba & 0xFFFFFFFF), + t->ref_tag, cmd, scst_get_opcode_name(cmd), + (long long)lba, dev->virt_name); + scst_dif_acc_ref_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_ref_tag_check_failed)); + res = -EIO; + goto out_put; + } + } + + /* Skip CRC check for internal commands */ + if ((checks & SCST_DIF_CHECK_GUARD_TAG) && + !cmd->internal) { + __be16 crc = crc_fn(cur_buf, block_size); + if (t->guard_tag != crc) { + PRINT_WARNING("GUARD TAG check failed, " + "expected 0x%x, seeing 0x%x " + "(cmd %p (op %s), lba %lld, " + "dev %s)", crc, t->guard_tag, cmd, + scst_get_opcode_name(cmd), (long long)lba, + dev->virt_name); + scst_dif_acc_guard_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_guard_check_failed)); + res = -EIO; + goto out_put; + } + } + +next: + cur_buf += dev->block_size; + lba++; + + t++; + tags_len -= tag_size; + if (tags_len == 0) { + scst_put_dif_buf(cmd, tags_buf); + tags_buf = NULL; + } + } + + scst_put_buf(cmd, buf); + len = scst_get_buf_next(cmd, &buf); + } + + EXTRACHECKS_BUG_ON(tags_buf != NULL); + +out: + TRACE_EXIT_RES(res); + return res; + +out_put: + scst_put_buf(cmd, buf); + scst_put_dif_buf(cmd, tags_buf); + goto out; +} + +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS +static void scst_check_fail_ref_tag(struct scst_cmd *cmd) +{ + enum scst_dif_actions checks = scst_get_dif_checks(cmd->cmd_dif_actions); + + if (((cmd->dev->dev_dif_mode & SCST_DIF_MODE_TGT) == 0) && + (checks & SCST_DIF_CHECK_REF_TAG)) { + TRACE(TRACE_SCSI|TRACE_MINOR, "No tgt dif_mode, failing " + "cmd %p with ref tag check failed", cmd); + scst_dif_acc_ref_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_ref_tag_check_failed)); + } + return; +} + +static void scst_check_fail_guard_tag(struct scst_cmd *cmd) +{ + enum scst_dif_actions checks = scst_get_dif_checks(cmd->cmd_dif_actions); + + if (((cmd->dev->dev_dif_mode & SCST_DIF_MODE_TGT) == 0) && + (checks & SCST_DIF_CHECK_GUARD_TAG)) { + TRACE(TRACE_SCSI|TRACE_MINOR, "No tgt dif_mode, failing " + "cmd %p with guard tag check failed", cmd); + scst_dif_acc_guard_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_guard_check_failed)); + } + return; +} +#endif + +static int scst_generate_dif_type1(struct scst_cmd *cmd) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + int len, tags_len = 0, tag_size = 1 << SCST_DIF_TAG_SHIFT; + struct scatterlist *tags_sg = NULL; + uint8_t *buf, *tags_buf = NULL; + struct t10_pi_tuple *t = NULL; /* to silence compiler warning */ + uint64_t lba = cmd->lba; + int block_size = dev->block_size, block_shift = dev->block_shift; + __be16 (*crc_fn)(const void *buffer, unsigned int len); + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type != 1); + +#ifdef CONFIG_SCST_EXTRACHECKS + switch (scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions))) { + case SCST_DIF_ACTION_INSERT: + break; + default: + EXTRACHECKS_BUG_ON(1); + break; + } +#endif + + crc_fn = cmd->tgt_dev->tgt_dev_dif_crc_fn; + + len = scst_get_buf_first(cmd, &buf); + while (len > 0) { + int i; + uint8_t *cur_buf = buf; + + TRACE_DBG("len %d", len); + + for (i = 0; i < (len >> block_shift); i++) { + TRACE_DBG("lba %lld, tags_len %d", (long long)lba, tags_len); + + if (tags_buf == NULL) { + tags_buf = scst_get_dif_buf(cmd, &tags_sg, &tags_len); + TRACE_DBG("tags_sg %p, tags_buf %p, tags_len %d", + tags_sg, tags_buf, tags_len); + EXTRACHECKS_BUG_ON(tags_len <= 0); + t = (struct t10_pi_tuple *)tags_buf; + } + + t->app_tag = dev->dev_dif_static_app_tag; + t->ref_tag = cpu_to_be32(lba & 0xFFFFFFFF); + t->guard_tag = crc_fn(cur_buf, block_size); + +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS + switch (cmd->cmd_corrupt_dif_tag) { + case 0: + break; + case 1: + if (lba == cmd->lba) { + if (cmd->cdb[1] & 0x80) { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting ref tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->ref_tag = cpu_to_be32(0xebfeedad); + scst_check_fail_ref_tag(cmd); + } else { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting guard tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->guard_tag = cpu_to_be16(0xebed); + scst_check_fail_guard_tag(cmd); + } + } + break; + case 2: + if (lba == (cmd->lba + 1)) { + if (cmd->cdb[1] & 0x80) { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting ref tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->ref_tag = cpu_to_be32(0xebfeedad); + scst_check_fail_ref_tag(cmd); + } else { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting guard tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->guard_tag = cpu_to_be16(0xebed); + scst_check_fail_guard_tag(cmd); + } + } + break; + case 3: + if (lba == (cmd->lba + 2)) { + if (cmd->cdb[1] & 0x80) { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting ref tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->ref_tag = cpu_to_be32(0xebfeedad); + scst_check_fail_ref_tag(cmd); + } else { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting guard tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->guard_tag = cpu_to_be16(0xebed); + scst_check_fail_guard_tag(cmd); + } + } + break; + case 4: + if (lba == (cmd->lba + ((cmd->data_len >> dev->block_shift) >> 1))) { + if (cmd->cdb[1] & 0x80) { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting ref tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->ref_tag = cpu_to_be32(0xebfeedad); + scst_check_fail_ref_tag(cmd); + } else { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting guard tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->guard_tag = cpu_to_be16(0xebed); + scst_check_fail_guard_tag(cmd); + } + } + break; + case 5: + if (lba == (cmd->lba + ((cmd->data_len >> dev->block_shift) - 3))) { + if (cmd->cdb[1] & 0x80) { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting ref tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->ref_tag = cpu_to_be32(0xebfeedad); + scst_check_fail_ref_tag(cmd); + } else { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting guard tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->guard_tag = cpu_to_be16(0xebed); + scst_check_fail_guard_tag(cmd); + } + } + break; + case 6: + if (lba == (cmd->lba + ((cmd->data_len >> dev->block_shift) - 2))) { + if (cmd->cdb[1] & 0x80) { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting ref tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->ref_tag = cpu_to_be32(0xebfeedad); + scst_check_fail_ref_tag(cmd); + } else { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting guard tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->guard_tag = cpu_to_be16(0xebed); + scst_check_fail_guard_tag(cmd); + } + } + break; + case 7: + if (lba == (cmd->lba + ((cmd->data_len >> dev->block_shift) - 1))) { + if (cmd->cdb[1] & 0x80) { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting ref tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->ref_tag = cpu_to_be32(0xebfeedad); + scst_check_fail_ref_tag(cmd); + } else { + TRACE(TRACE_SCSI|TRACE_MINOR, + "Corrupting guard tag at lba " + "%lld (case %d, cmd %p)", + (long long)lba, + cmd->cmd_corrupt_dif_tag, cmd); + t->guard_tag = cpu_to_be16(0xebed); + scst_check_fail_guard_tag(cmd); + } + } + break; + } +#endif + cur_buf += dev->block_size; + lba++; + + t++; + tags_len -= tag_size; + if (tags_len == 0) { + scst_put_dif_buf(cmd, tags_buf); + tags_buf = NULL; + } + } + + scst_put_buf(cmd, buf); + len = scst_get_buf_next(cmd, &buf); + } + + EXTRACHECKS_BUG_ON(tags_buf != NULL); + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_do_dif(struct scst_cmd *cmd, + int (*generate_fn)(struct scst_cmd *cmd), + int (*verify_fn)(struct scst_cmd *cmd)) +{ + int res; + enum scst_dif_actions action = scst_get_dif_action( + scst_get_scst_dif_actions(cmd->cmd_dif_actions)); + + TRACE_ENTRY(); + + TRACE_DBG("cmd %p, action %d", cmd, action); + + switch (action) { + case SCST_DIF_ACTION_PASS: + /* Nothing to do */ + TRACE_DBG("PASS DIF action, skipping (cmd %p)", cmd); + res = 0; + break; + + case SCST_DIF_ACTION_STRIP: + case SCST_DIF_ACTION_PASS_CHECK: + { + enum scst_dif_actions checks = scst_get_dif_checks(cmd->cmd_dif_actions); + if (likely(checks != SCST_DIF_ACTION_NONE)) + res = verify_fn(cmd); + else + res = 0; + break; + } + + case SCST_DIF_ACTION_INSERT: + res = generate_fn(cmd); + break; + + default: + EXTRACHECKS_BUG_ON(1); + /* go through */ + case SCST_DIF_ACTION_NONE: + /* Nothing to do */ + TRACE_DBG("NONE DIF action, skipping (cmd %p)", cmd); + res = 0; + break; + } + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_dif_type1(struct scst_cmd *cmd) +{ + int res; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(cmd->dev->dev_dif_type != 1); + + res = scst_do_dif(cmd, scst_generate_dif_type1, scst_verify_dif_type1); + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_verify_dif_type2(struct scst_cmd *cmd) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + enum scst_dif_actions checks = scst_get_dif_checks(cmd->cmd_dif_actions); + int len, tags_len = 0, tag_size = 1 << SCST_DIF_TAG_SHIFT; + struct scatterlist *tags_sg = NULL; + uint8_t *buf, *tags_buf = NULL; + const struct t10_pi_tuple *t = NULL; /* to silence compiler warning */ + uint64_t lba = cmd->lba; + int block_size = dev->block_size, block_shift = dev->block_shift; + __be16 (*crc_fn)(const void *buffer, unsigned int len); + uint32_t ref_tag = scst_cmd_get_dif_exp_ref_tag(cmd); + /* Let's keep both in BE */ + __be16 app_tag_mask = cpu_to_be16(scst_cmd_get_dif_app_tag_mask(cmd)); + __be16 app_tag_masked = cpu_to_be16(scst_cmd_get_dif_exp_app_tag(cmd)) & app_tag_mask; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type != 2); + +#ifdef CONFIG_SCST_EXTRACHECKS + switch (scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions))) { + case SCST_DIF_ACTION_STRIP: + case SCST_DIF_ACTION_PASS_CHECK: + break; + default: + EXTRACHECKS_BUG_ON(1); + break; + } + EXTRACHECKS_BUG_ON(checks == SCST_DIF_ACTION_NONE); +#endif + + crc_fn = cmd->tgt_dev->tgt_dev_dif_crc_fn; + + len = scst_get_buf_first(cmd, &buf); + while (len > 0) { + int i; + uint8_t *cur_buf = buf; + + for (i = 0; i < (len >> block_shift); i++) { + if (tags_buf == NULL) { + tags_buf = scst_get_dif_buf(cmd, &tags_sg, &tags_len); + EXTRACHECKS_BUG_ON(tags_len <= 0); + t = (struct t10_pi_tuple *)tags_buf; + } + + if (t->app_tag == SCST_DIF_NO_CHECK_ALL_APP_TAG) { + TRACE_DBG("Skipping tag (cmd %p)", cmd); + goto next; + } + + if (checks & SCST_DIF_CHECK_APP_TAG) { + if ((t->app_tag & app_tag_mask) != app_tag_masked) { + PRINT_WARNING("APP TAG check failed, " + "expected 0x%x, seeing " + "0x%x (cmd %p (op %s), dev %s)", + app_tag_masked, t->app_tag & app_tag_mask, + cmd, scst_get_opcode_name(cmd), dev->virt_name); + scst_dif_acc_app_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_app_tag_check_failed)); + res = -EIO; + goto out_put; + } + } + + if (checks & SCST_DIF_CHECK_REF_TAG) { + if (t->ref_tag != cpu_to_be32(ref_tag)) { + PRINT_WARNING("REF TAG check failed, " + "expected 0x%x, seeing " + "0x%x (cmd %p (op %s), dev %s)", + cpu_to_be32(ref_tag), + t->ref_tag, cmd, scst_get_opcode_name(cmd), + dev->virt_name); + scst_dif_acc_ref_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_ref_tag_check_failed)); + res = -EIO; + goto out_put; + } + } + + /* Skip CRC check for internal commands */ + if ((checks & SCST_DIF_CHECK_GUARD_TAG) && + !cmd->internal) { + __be16 crc = crc_fn(cur_buf, block_size); + if (t->guard_tag != crc) { + PRINT_WARNING("GUARD TAG check failed, " + "expected 0x%x, seeing 0x%x " + "(cmd %p (op %s), lba %lld, " + "dev %s)", crc, t->guard_tag, cmd, + scst_get_opcode_name(cmd), (long long)lba, + dev->virt_name); + scst_dif_acc_guard_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_guard_check_failed)); + res = -EIO; + goto out_put; + } + } + +next: + cur_buf += dev->block_size; + lba++; + ref_tag++; + + t++; + tags_len -= tag_size; + if (tags_len == 0) { + scst_put_dif_buf(cmd, tags_buf); + tags_buf = NULL; + } + } + + scst_put_buf(cmd, buf); + len = scst_get_buf_next(cmd, &buf); + } + + EXTRACHECKS_BUG_ON(tags_buf != NULL); + +out: + TRACE_EXIT_RES(res); + return res; + +out_put: + scst_put_buf(cmd, buf); + scst_put_dif_buf(cmd, tags_buf); + goto out; +} + +static int scst_generate_dif_type2(struct scst_cmd *cmd) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + int len, tags_len = 0, tag_size = 1 << SCST_DIF_TAG_SHIFT; + struct scatterlist *tags_sg = NULL; + uint8_t *buf, *tags_buf = NULL; + struct t10_pi_tuple *t = NULL; /* to silence compiler warning */ + int block_size = dev->block_size, block_shift = dev->block_shift; + __be16 (*crc_fn)(const void *buffer, unsigned int len); + uint32_t ref_tag = scst_cmd_get_dif_exp_ref_tag(cmd); + /* Let's keep both in BE */ + __be16 app_tag_mask = cpu_to_be16(scst_cmd_get_dif_app_tag_mask(cmd)); + __be16 app_tag_masked = cpu_to_be16(scst_cmd_get_dif_exp_app_tag(cmd)) & app_tag_mask; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type != 2); + +#ifdef CONFIG_SCST_EXTRACHECKS + switch (scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions))) { + case SCST_DIF_ACTION_INSERT: + break; + default: + EXTRACHECKS_BUG_ON(1); + break; + } +#endif + + crc_fn = cmd->tgt_dev->tgt_dev_dif_crc_fn; + + len = scst_get_buf_first(cmd, &buf); + while (len > 0) { + int i; + uint8_t *cur_buf = buf; + + TRACE_DBG("len %d", len); + + for (i = 0; i < (len >> block_shift); i++) { + TRACE_DBG("tags_len %d", tags_len); + + if (tags_buf == NULL) { + tags_buf = scst_get_dif_buf(cmd, &tags_sg, &tags_len); + TRACE_DBG("tags_sg %p, tags_buf %p, tags_len %d", + tags_sg, tags_buf, tags_len); + EXTRACHECKS_BUG_ON(tags_len <= 0); + t = (struct t10_pi_tuple *)tags_buf; + } + + t->app_tag = app_tag_masked; + t->ref_tag = cpu_to_be32(ref_tag); + t->guard_tag = crc_fn(cur_buf, block_size); + + cur_buf += dev->block_size; + ref_tag++; + + t++; + tags_len -= tag_size; + if (tags_len == 0) { + scst_put_dif_buf(cmd, tags_buf); + tags_buf = NULL; + } + } + + scst_put_buf(cmd, buf); + len = scst_get_buf_next(cmd, &buf); + } + + EXTRACHECKS_BUG_ON(tags_buf != NULL); + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_dif_type2(struct scst_cmd *cmd) +{ + int res; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(cmd->dev->dev_dif_type != 2); + + res = scst_do_dif(cmd, scst_generate_dif_type2, scst_verify_dif_type2); + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_verify_dif_type3(struct scst_cmd *cmd) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + enum scst_dif_actions checks = scst_get_dif_checks(cmd->cmd_dif_actions); + int len, tags_len = 0, tag_size = 1 << SCST_DIF_TAG_SHIFT; + struct scatterlist *tags_sg = NULL; + uint8_t *buf, *tags_buf = NULL; + const struct t10_pi_tuple *t = NULL; /* to silence compiler warning */ + uint64_t lba = cmd->lba; + int block_size = dev->block_size, block_shift = dev->block_shift; + __be16 (*crc_fn)(const void *buffer, unsigned int len); + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type != 3); + +#ifdef CONFIG_SCST_EXTRACHECKS + switch (scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions))) { + case SCST_DIF_ACTION_STRIP: + case SCST_DIF_ACTION_PASS_CHECK: + break; + default: + EXTRACHECKS_BUG_ON(1); + break; + } + EXTRACHECKS_BUG_ON(checks == SCST_DIF_ACTION_NONE); +#endif + + crc_fn = cmd->tgt_dev->tgt_dev_dif_crc_fn; + + len = scst_get_buf_first(cmd, &buf); + while (len > 0) { + int i; + uint8_t *cur_buf = buf; + + for (i = 0; i < (len >> block_shift); i++) { + if (tags_buf == NULL) { + tags_buf = scst_get_dif_buf(cmd, &tags_sg, &tags_len); + EXTRACHECKS_BUG_ON(tags_len <= 0); + t = (struct t10_pi_tuple *)tags_buf; + } + + if ((t->app_tag == SCST_DIF_NO_CHECK_ALL_APP_TAG) && + (t->ref_tag == SCST_DIF_NO_CHECK_ALL_REF_TAG)) { + TRACE_DBG("Skipping tag (cmd %p)", cmd); + goto next; + } + + if (checks & SCST_DIF_CHECK_APP_TAG) { + if (t->app_tag != dev->dev_dif_static_app_tag) { + PRINT_WARNING("APP TAG check failed, " + "expected 0x%x, seeing " + "0x%x (cmd %p (op %s), dev %s)", + dev->dev_dif_static_app_tag, + t->app_tag, cmd, scst_get_opcode_name(cmd), + dev->virt_name); + scst_dif_acc_app_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_app_tag_check_failed)); + res = -EIO; + goto out_put; + } + } + + if (checks & SCST_DIF_CHECK_REF_TAG) { + if (t->ref_tag != dev->dev_dif_static_app_ref_tag) { + PRINT_WARNING("REF TAG check failed, " + "expected 0x%x, seeing " + "0x%x (cmd %p (op %s), dev %s)", + dev->dev_dif_static_app_ref_tag, + t->ref_tag, cmd, scst_get_opcode_name(cmd), + dev->virt_name); + scst_dif_acc_ref_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_ref_tag_check_failed)); + res = -EIO; + goto out_put; + } + } + + /* Skip CRC check for internal commands */ + if ((checks & SCST_DIF_CHECK_GUARD_TAG) && + !cmd->internal) { + __be16 crc = crc_fn(cur_buf, block_size); + if (t->guard_tag != crc) { + PRINT_WARNING("GUARD TAG check failed, " + "expected 0x%x, seeing 0x%x " + "(cmd %p (op %s), lba %lld, " + "dev %s)", crc, t->guard_tag, cmd, + scst_get_opcode_name(cmd), (long long)lba, + dev->virt_name); + scst_dif_acc_guard_check_failed_scst(cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_logical_block_guard_check_failed)); + res = -EIO; + goto out_put; + } + } + +next: + cur_buf += dev->block_size; + lba++; + + t++; + tags_len -= tag_size; + if (tags_len == 0) { + scst_put_dif_buf(cmd, tags_buf); + tags_buf = NULL; + } + } + + scst_put_buf(cmd, buf); + len = scst_get_buf_next(cmd, &buf); + } + + EXTRACHECKS_BUG_ON(tags_buf != NULL); + +out: + TRACE_EXIT_RES(res); + return res; + +out_put: + scst_put_buf(cmd, buf); + scst_put_dif_buf(cmd, tags_buf); + goto out; +} + +static int scst_generate_dif_type3(struct scst_cmd *cmd) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + int len, tags_len = 0, tag_size = 1 << SCST_DIF_TAG_SHIFT; + struct scatterlist *tags_sg = NULL; + uint8_t *buf, *tags_buf = NULL; + struct t10_pi_tuple *t = NULL; /* to silence compiler warning */ + int block_size = dev->block_size, block_shift = dev->block_shift; + __be16 (*crc_fn)(const void *buffer, unsigned int len); + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type != 3); + +#ifdef CONFIG_SCST_EXTRACHECKS + switch (scst_get_dif_action(scst_get_scst_dif_actions(cmd->cmd_dif_actions))) { + case SCST_DIF_ACTION_INSERT: + break; + default: + EXTRACHECKS_BUG_ON(1); + break; + } +#endif + + crc_fn = cmd->tgt_dev->tgt_dev_dif_crc_fn; + + len = scst_get_buf_first(cmd, &buf); + while (len > 0) { + int i; + uint8_t *cur_buf = buf; + + TRACE_DBG("len %d", len); + + for (i = 0; i < (len >> block_shift); i++) { + TRACE_DBG("tags_len %d", tags_len); + + if (tags_buf == NULL) { + tags_buf = scst_get_dif_buf(cmd, &tags_sg, &tags_len); + TRACE_DBG("tags_sg %p, tags_buf %p, tags_len %d", + tags_sg, tags_buf, tags_len); + EXTRACHECKS_BUG_ON(tags_len <= 0); + t = (struct t10_pi_tuple *)tags_buf; + } + + t->app_tag = dev->dev_dif_static_app_tag; + t->ref_tag = dev->dev_dif_static_app_ref_tag; + t->guard_tag = crc_fn(cur_buf, block_size); + + cur_buf += dev->block_size; + + t++; + tags_len -= tag_size; + if (tags_len == 0) { + scst_put_dif_buf(cmd, tags_buf); + tags_buf = NULL; + } + } + + scst_put_buf(cmd, buf); + len = scst_get_buf_next(cmd, &buf); + } + + EXTRACHECKS_BUG_ON(tags_buf != NULL); + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_dif_type3(struct scst_cmd *cmd) +{ + int res; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(cmd->dev->dev_dif_type != 3); + + res = scst_do_dif(cmd, scst_generate_dif_type3, scst_verify_dif_type3); + + TRACE_EXIT_RES(res); + return res; +} + +static void scst_init_dif_checks(struct scst_device *dev) +{ + switch (dev->dev_dif_type) { + case 0: + dev->dif_app_chk = 0; + dev->dif_ref_chk = 0; + break; + case 1: + if (scst_dev_get_dif_static_app_tag(dev) != SCST_DIF_NO_CHECK_APP_TAG) + dev->dif_app_chk = SCST_DIF_CHECK_APP_TAG; + else + dev->dif_app_chk = 0; + dev->dif_ref_chk = SCST_DIF_CHECK_REF_TAG; + break; + case 2: + dev->dif_app_chk = dev->ato ? SCST_DIF_CHECK_APP_TAG : 0; + dev->dif_ref_chk = SCST_DIF_CHECK_REF_TAG; + break; + case 3: + if (scst_dev_get_dif_static_app_tag_combined(dev) != SCST_DIF_NO_CHECK_APP_TAG) { + dev->dif_app_chk = SCST_DIF_CHECK_APP_TAG; + dev->dif_ref_chk = SCST_DIF_CHECK_REF_TAG; + } else { + dev->dif_app_chk = 0; + dev->dif_ref_chk = 0; + } + break; + default: + WARN_ON(1); + break; + } + return; +} + +/** + * scst_dev_set_dif_static_app_tag_combined() - sets static app tag + * + * Description: + * Sets static app tag for both APP TAG and REF TAG (DIF mode 3) + */ +void scst_dev_set_dif_static_app_tag_combined( + struct scst_device *dev, __be64 app_tag) +{ + uint64_t a = be64_to_cpu(app_tag); + + dev->dev_dif_static_app_tag = cpu_to_be16(a & 0xFFFF); + if (dev->dev_dif_type == 3) + dev->dev_dif_static_app_ref_tag = cpu_to_be32((a >> 16) & 0xFFFFFFFF); + scst_init_dif_checks(dev); + return; +} +EXPORT_SYMBOL_GPL(scst_dev_set_dif_static_app_tag_combined); + +static int scst_init_dif_actions(struct scst_device *dev) +{ + int res = 0; + + TRACE_ENTRY(); + + BUILD_BUG_ON(SCST_DIF_ACTION_NONE != 0); + + if ((dev->dev_dif_type == 0) || (dev->dev_dif_mode == 0)) { + dev->dev_dif_rd_actions = SCST_DIF_ACTION_NONE; + dev->dev_dif_wr_actions = SCST_DIF_ACTION_NONE; + dev->dev_dif_rd_prot0_actions = SCST_DIF_ACTION_NONE; + dev->dev_dif_wr_prot0_actions = SCST_DIF_ACTION_NONE; + goto out; + } + + if ((dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) && + !(dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE)) { + PRINT_ERROR("DEV CHECK is not allowed without DEV STORE! " + "(dev %s)", dev->virt_name); + res = -EINVAL; + goto out; + } + + /* + * COMMON RULES + * ============ + * + * 1. For SCST STRIP and PASS_CHECK as well as PASS and NONE are + * the same. + * + * 2. For dev handlers STRIP and INSERT are invalid, PASS and NONE are + * the same. Also, PASS and PASS_CHECK are equal regarding STORE, + * if STORE is configured. + */ + + BUILD_BUG_ON(SCST_DIF_MODE_DEV != (SCST_DIF_MODE_DEV_CHECK | SCST_DIF_MODE_DEV_STORE)); + + if (dev->dev_dif_mode & SCST_DIF_MODE_TGT) { + if (dev->dev_dif_mode & SCST_DIF_MODE_SCST) { + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV) { /* TGT, SCST, DEV */ + scst_set_tgt_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + scst_set_scst_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + + if (dev->dpicz) { + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + } else { + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_STRIP); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS); + } + + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_INSERT); + scst_set_scst_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS); + } else { /* TGT, SCST, no DEV */ + scst_set_tgt_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + scst_set_scst_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_INSERT); + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_NONE); + + if (dev->dpicz) { + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + } else { + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_STRIP); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_INSERT); + } + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_STRIP); + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_NONE); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_INSERT); + scst_set_scst_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_STRIP); + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + } + } else { /* TGT, no SCST */ + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV) { /* TGT, no SCST, DEV */ + scst_set_tgt_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + scst_set_scst_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + } else { + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + } + + if (dev->dpicz) { + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + } else { + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_STRIP); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS); + } + + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_INSERT); + scst_set_scst_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS); + } else { /* TGT, no SCST, no DEV */ + scst_set_tgt_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_INSERT); + scst_set_scst_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_NONE); + + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + +#if 1 /* + * Workaround for Emulex ASIC errata to pass Oracle certification. + * + * Emulex ASIC in the STRIP mode can't distinguish between different types of + * PI tags mismatches (reference, guard, application), hence the Oracle + * certification fails. Workaround is to get the tags data in the memory, so + * then, if HW is signaling that there is some tag mismatch, manually recheck + * in the driver, then set correct tag mismatch sense. + */ + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); +#else + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_STRIP); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_NONE); +#endif + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_NONE); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + } + } + } else { /* No TGT */ + if (dev->dev_dif_mode & SCST_DIF_MODE_SCST) { /* No TGT, SCST */ + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV) { /* No TGT, SCST, DEV */ + scst_set_tgt_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + scst_set_scst_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + if (dev->dpicz) { + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + } else { + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_STRIP); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS); + } + + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_INSERT); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS); + } else { /* No TGT, SCST, no DEV */ + scst_set_tgt_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + scst_set_scst_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_INSERT); + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_NONE); + + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_STRIP); + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_NONE); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + } + } else { /* No TGT, no SCST */ + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV) { /* No TGT, no SCST, DEV */ + scst_set_tgt_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + scst_set_scst_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_rd_actions, SCST_DIF_ACTION_PASS); + + scst_set_tgt_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + if (dev->dpicz) { + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_NONE); + } else { + scst_set_scst_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_rd_prot0_actions, SCST_DIF_ACTION_PASS); + } + + scst_set_tgt_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + scst_set_scst_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_actions, SCST_DIF_ACTION_PASS); + + scst_set_tgt_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_NONE); + scst_set_scst_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_INSERT); + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) { + /* STORE might be set as well */ + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS_CHECK); + } else + scst_set_dev_dif_action(&dev->dev_dif_wr_prot0_actions, SCST_DIF_ACTION_PASS); + } else { /* No TGT, no SCST, no DEV */ + sBUG_ON(1); + } + } + } + + TRACE_DBG("rd_actions %x, rd_prot0_actions %x, wr_actions %x, " + "wr_prot0_actions %x", dev->dev_dif_rd_actions, + dev->dev_dif_rd_prot0_actions, dev->dev_dif_wr_actions, + dev->dev_dif_wr_prot0_actions); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/** + * scst_set_dif_params() - sets DIF parameters + * + * Description: + * Sets DIF parameters for device + * + * Returns: 0 on success, negative error code otherwise. + */ +int scst_set_dif_params(struct scst_device *dev, enum scst_dif_mode dif_mode, + int dif_type) +{ + int res = -EINVAL; + + TRACE_ENTRY(); + + BUILD_BUG_ON(SCST_DIF_ACTION_NONE != 0); + + if (((dif_mode & ~(SCST_DIF_MODE_TGT|SCST_DIF_MODE_SCST|SCST_DIF_MODE_DEV)) != 0)) { + PRINT_ERROR("Invalid DIF mode %x", dif_mode); + goto out; + } + + if ((dif_type < 0) || (dif_type > 3)) { + PRINT_ERROR("DIF type %d not supported", dif_type); + goto out; + } + + if ((dif_mode == 0) && (dif_type != 0)) { + PRINT_ERROR("With DIF mode 0 DIF type must be 0 (dev %s)", + dev->virt_name); + goto out; + } + + dev->dev_dif_mode = dif_mode; + dev->dev_dif_type = dif_type; + + if (dif_type == 0) { + TRACE_DBG("DIF type 0, ignoring DIF mode"); + goto out_none; + } + + if (dif_mode == 0) { + TRACE_DBG("DIF mode 0"); + goto out_none; + } + + scst_init_dif_checks(dev); + + if (dif_mode & SCST_DIF_MODE_SCST) { + switch (dif_type) { + case 1: + dev->dev_dif_fn = scst_dif_type1; + break; + case 2: + dev->dev_dif_fn = scst_dif_type2; + break; + case 3: + dev->dev_dif_fn = scst_dif_type3; + break; + default: + sBUG_ON(1); + break; + } + } else { + if ((dif_type == 1) && !(dif_mode & SCST_DIF_MODE_TGT)) + dev->dev_dif_fn = scst_dif_type1; + else if ((dif_type == 1) && (dif_mode & SCST_DIF_MODE_TGT)) + dev->dev_dif_fn = scst_dif_none_type1; + else + dev->dev_dif_fn = scst_dif_none; + } + + res = scst_init_dif_actions(dev); + if (res != 0) + goto out; + + res = scst_dev_sysfs_dif_create(dev); + if (res != 0) + goto out; + + PRINT_INFO("device %s: DIF mode %x, DIF type %d", dev->virt_name, + dev->dev_dif_mode, dev->dev_dif_type); + TRACE_DBG("app_chk %x, ref_chk %x", dev->dif_app_chk, dev->dif_ref_chk); + +out: + TRACE_EXIT_RES(res); + return res; + +out_none: + dev->dif_app_chk = 0; + if (dev->dev_dif_type == 3) + dev->dif_ref_chk = 0; + dev->dev_dif_static_app_tag = SCST_DIF_NO_CHECK_APP_TAG; + dev->dev_dif_static_app_ref_tag = SCST_DIF_NO_CHECK_APP_TAG; + dev->dev_dif_fn = scst_dif_none; + res = scst_init_dif_actions(dev); + goto out; +} +EXPORT_SYMBOL_GPL(scst_set_dif_params); + +static int scst_dif_none(struct scst_cmd *cmd) +{ + int res = 0; + + TRACE_ENTRY(); + + /* Nothing to do */ + + TRACE_EXIT_RES(res); + return res; +} + +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS +static int scst_dif_none_type1(struct scst_cmd *cmd) +{ + int res = 0; + + TRACE_ENTRY(); + + if (unlikely(cmd->cmd_corrupt_dif_tag != 0)) { + EXTRACHECKS_BUG_ON(cmd->dev->dev_dif_type != 1); + res = scst_dif_type1(cmd); + } + + TRACE_EXIT_RES(res); + return res; +} +#endif + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int __scst_parse_rdprotect(struct scst_cmd *cmd, int rdprotect, + int rdprotect_offs) +{ + int res = 0; + const struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + TRACE_DBG("rdprotect %x", rdprotect); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type == 0); + + switch (rdprotect) { + case 0: + cmd->cmd_dif_actions = dev->dev_dif_rd_prot0_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + dev->dif_ref_chk; + break; + case 1: + case 5: + cmd->cmd_dif_actions = dev->dev_dif_rd_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + dev->dif_ref_chk; + cmd->tgt_dif_data_expected = 1; + break; + case 2: + cmd->cmd_dif_actions = dev->dev_dif_rd_actions | + dev->dif_app_chk | dev->dif_ref_chk; + cmd->tgt_dif_data_expected = 1; + break; + case 3: + cmd->cmd_dif_actions = dev->dev_dif_rd_actions; + cmd->tgt_dif_data_expected = 1; + break; + case 4: + cmd->cmd_dif_actions = dev->dev_dif_rd_actions | + dev->dif_app_chk; + cmd->tgt_dif_data_expected = 1; + break; + default: + TRACE(TRACE_SCSI|TRACE_MINOR, "Invalid RDPROTECT value %x " + "(dev %s, cmd %p)", rdprotect, dev->virt_name, cmd); + scst_set_invalid_field_in_cdb(cmd, rdprotect_offs, + 5|SCST_INVAL_FIELD_BIT_OFFS_VALID); + res = EINVAL; + goto out; + } + + TRACE_DBG("cmd_dif_actions %x, tgt_dif_data_expected %d (cmd %p)", + cmd->cmd_dif_actions, cmd->tgt_dif_data_expected, cmd); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int scst_parse_rdprotect(struct scst_cmd *cmd) +{ + int res = 0; + int rdprotect = (cmd->cdb[1] & 0xE0) >> 5; + const struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + if (dev->dev_dif_mode == SCST_DIF_MODE_NONE) { + if (likely(rdprotect == 0)) + goto out; + + TRACE(TRACE_SCSI|TRACE_MINOR, "RDPROTECT %x and no DIF " + "device %s (cmd %p)", rdprotect, + dev->virt_name, cmd); + scst_set_invalid_field_in_cdb(cmd, 1, + 5|SCST_INVAL_FIELD_BIT_OFFS_VALID); + res = EINVAL; + goto out; + } + + if (unlikely((dev->dev_dif_type == 2) && (rdprotect != 0))) { + TRACE(TRACE_SCSI|TRACE_MINOR, "Non-32-byte CDBs with non-zero " + "rdprotect (%d) are not allowed in DIF type 2 " + "(dev %s, cmd %p, op %s)", rdprotect, dev->virt_name, + cmd, scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + res = EINVAL; + goto out; + } + +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS + if (unlikely(cmd->cmd_corrupt_dif_tag != 0)) { + if ((cmd->dev->dev_dif_type == 1) && + ((dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) == 0)) { + TRACE_DBG("cmd_corrupt_dif_tag %x, rdprotect %x, cmd %p", + cmd->cmd_corrupt_dif_tag, rdprotect, cmd); + /* Reset the highest bit used to choose which tag to corrupt */ + rdprotect &= 3; + } else { + /* Restore it */ + cmd->cmd_corrupt_dif_tag = 0; + } + } +#endif + + res = __scst_parse_rdprotect(cmd, rdprotect, 1); + +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS + if (unlikely(cmd->cmd_corrupt_dif_tag != 0)) { + if (res == 0) { + TRACE_DBG("Corrupt DIF tag, (re)set cmd_dif_actions " + "(cmd %p)", cmd); + /* + * Then (re)set it just in case if dif_mode is tgt-only. + * It's OK, because we have ensured that dif_mode + * doesn't contain dev_store. + */ + scst_set_scst_dif_action(&cmd->cmd_dif_actions, + SCST_DIF_ACTION_INSERT); + if (scst_get_dif_action(scst_get_read_dif_tgt_actions(cmd)) == SCST_DIF_ACTION_INSERT) + scst_set_tgt_dif_action(&cmd->cmd_dif_actions, + SCST_DIF_ACTION_PASS_CHECK); + } else + TRACE_DBG("parse rdprotect failed: %d", res); + } +#endif + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int scst_parse_rdprotect32(struct scst_cmd *cmd) +{ + int res = 0; + int rdprotect = (cmd->cdb[10] & 0xE0) >> 5; + const struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + if (unlikely(dev->dev_dif_type != 2)) { + TRACE(TRACE_SCSI|TRACE_MINOR, "32-byte CDBs are not " + "allowed in DIF type %d (dev %s, cmd %p, op %s)", + dev->dev_dif_type, dev->virt_name, cmd, + scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + res = EINVAL; + goto out; + } + + res = __scst_parse_rdprotect(cmd, rdprotect, 10); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int __scst_parse_wrprotect(struct scst_cmd *cmd, int wrprotect, + int wrprotect_offs) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + TRACE_DBG("wrprotect %x", wrprotect); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type == 0); + + switch (wrprotect) { + case 0: + cmd->cmd_dif_actions = dev->dev_dif_wr_prot0_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + dev->dif_ref_chk; + break; + case 1: + cmd->cmd_dif_actions = dev->dev_dif_wr_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + dev->dif_ref_chk; + cmd->tgt_dif_data_expected = 1; + break; + case 2: + cmd->cmd_dif_actions = dev->dev_dif_wr_actions | + dev->dif_app_chk | dev->dif_ref_chk; + cmd->tgt_dif_data_expected = 1; + break; + case 3: + cmd->cmd_dif_actions = dev->dev_dif_wr_actions; + cmd->tgt_dif_data_expected = 1; + break; + case 4: + cmd->cmd_dif_actions = dev->dev_dif_wr_actions | + SCST_DIF_CHECK_GUARD_TAG; + cmd->tgt_dif_data_expected = 1; + break; + case 5: + cmd->cmd_dif_actions = dev->dev_dif_wr_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + dev->dif_ref_chk; + cmd->tgt_dif_data_expected = 1; + break; + default: + TRACE(TRACE_SCSI|TRACE_MINOR, "Invalid WRPROTECT value %x " + "(dev %s, cmd %p)", wrprotect, dev->virt_name, cmd); + scst_set_invalid_field_in_cdb(cmd, wrprotect_offs, + 5|SCST_INVAL_FIELD_BIT_OFFS_VALID); + res = EINVAL; + goto out; + } + + TRACE_DBG("cmd_dif_actions %x, tgt_dif_data_expected %d (cmd %p)", + cmd->cmd_dif_actions, cmd->tgt_dif_data_expected, cmd); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int scst_parse_wrprotect(struct scst_cmd *cmd) +{ + int res = 0; + int wrprotect = (cmd->cdb[1] & 0xE0) >> 5; + const struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + if (dev->dev_dif_mode == SCST_DIF_MODE_NONE) { + if (likely(wrprotect == 0)) + goto out; + + TRACE(TRACE_SCSI|TRACE_MINOR, "WRPROTECT %x and no DIF " + "device %s (cmd %p)", wrprotect, + dev->virt_name, cmd); + scst_set_invalid_field_in_cdb(cmd, 1, + 5|SCST_INVAL_FIELD_BIT_OFFS_VALID); + res = EINVAL; + goto out; + } + + if (unlikely((dev->dev_dif_type == 2) && (wrprotect != 0))) { + TRACE(TRACE_SCSI|TRACE_MINOR, "Non-32-byte CDBs with non-zero " + "wrprotect (%d) are not allowed in DIF type 2 " + "(dev %s, cmd %p, op %s)", wrprotect, dev->virt_name, + cmd, scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + res = EINVAL; + goto out; + } + + res = __scst_parse_wrprotect(cmd, wrprotect, 1); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int scst_parse_wrprotect32(struct scst_cmd *cmd) +{ + int res = 0; + int wrprotect = (cmd->cdb[10] & 0xE0) >> 5; + const struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + if (unlikely(dev->dev_dif_type != 2)) { + TRACE(TRACE_SCSI|TRACE_MINOR, "32-byte CDBs are not " + "allowed in DIF type %d (dev %s, cmd %p, op %s)", + dev->dev_dif_type, dev->virt_name, cmd, + scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + res = EINVAL; + goto out; + } + + res = __scst_parse_wrprotect(cmd, wrprotect, 10); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int __scst_parse_vrprotect(struct scst_cmd *cmd, int vrprotect_offs) +{ + int res = 0; + struct scst_device *dev = cmd->dev; + int vrprotect = (cmd->cdb[vrprotect_offs] & 0xE0) >> 5; + int bytchk = (cmd->cdb[vrprotect_offs] & 6) >> 1; + + TRACE_ENTRY(); + + TRACE_DBG("vrprotect %x", vrprotect); + + EXTRACHECKS_BUG_ON(dev->dev_dif_type == 0); + + switch (bytchk) { + case 0: + switch (vrprotect) { + case 0: + if (dev->dpicz) { + cmd->cmd_dif_actions = 0; + break; + } + /* else go through */ + case 1: + case 5: + cmd->cmd_dif_actions = SCST_DIF_CHECK_GUARD_TAG | + dev->dif_app_chk | dev->dif_ref_chk; + break; + case 2: + cmd->cmd_dif_actions = dev->dif_app_chk | dev->dif_ref_chk; + break; + case 3: + cmd->cmd_dif_actions = 0; + break; + case 4: + cmd->cmd_dif_actions = SCST_DIF_CHECK_GUARD_TAG; + break; + default: + TRACE(TRACE_SCSI|TRACE_MINOR, "Invalid VRPROTECT value %x " + "(dev %s, cmd %p)", vrprotect, dev->virt_name, cmd); + scst_set_invalid_field_in_cdb(cmd, vrprotect_offs, + 5|SCST_INVAL_FIELD_BIT_OFFS_VALID); + res = EINVAL; + goto out; + } + break; + case 1: + case 3: + switch (vrprotect) { + case 0: + if (dev->dpicz) + cmd->cmd_dif_actions = 0; + else + cmd->cmd_dif_actions = dev->dev_dif_wr_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + dev->dif_ref_chk; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + cmd->cmd_dif_actions = 0; + break; + default: + TRACE(TRACE_SCSI|TRACE_MINOR, "Invalid VRPROTECT value %x " + "(dev %s, cmd %p)", vrprotect, dev->virt_name, cmd); + scst_set_invalid_field_in_cdb(cmd, vrprotect_offs, + 5|SCST_INVAL_FIELD_BIT_OFFS_VALID); + res = EINVAL; + goto out; + } + break; + default: + sBUG_ON(1); + break; + } + + TRACE_DBG("cmd_dif_actions %x, tgt_dif_data_expected %d (cmd %p, " + "bytchk %d)", cmd->cmd_dif_actions, cmd->tgt_dif_data_expected, + cmd, bytchk); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int scst_parse_vrprotect(struct scst_cmd *cmd) +{ + int res = 0; + int vrprotect = (cmd->cdb[1] & 0xE0) >> 5; + const struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + if (dev->dev_dif_mode == SCST_DIF_MODE_NONE) { + if (likely(vrprotect == 0)) + goto out; + + TRACE(TRACE_SCSI|TRACE_MINOR, "VRPROTECT %x and no DIF " + "device %s (cmd %p)", vrprotect, + dev->virt_name, cmd); + scst_set_invalid_field_in_cdb(cmd, 1, + 5|SCST_INVAL_FIELD_BIT_OFFS_VALID); + res = EINVAL; + goto out; + } + + if (unlikely((dev->dev_dif_type == 2) && (vrprotect != 0))) { + TRACE(TRACE_SCSI|TRACE_MINOR, "Non-32-byte CDBs with non-zero " + "vrprotect (%d) are not allowed in DIF type 2 " + "(dev %s, cmd %p, op %s)", vrprotect, + dev->virt_name, cmd, scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + res = EINVAL; + goto out; + } + + res = __scst_parse_vrprotect(cmd, 1); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns 0 on success or POSITIVE error code otherwise (to match + * scst_get_cdb_info() semantic) + */ +static int scst_parse_vrprotect32(struct scst_cmd *cmd) +{ + int res = 0; + const struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + if (unlikely(dev->dev_dif_type != 2)) { + TRACE(TRACE_SCSI|TRACE_MINOR, "32-byte CDBs are not " + "allowed in DIF type %d (dev %s, cmd %p, op %s)", + dev->dev_dif_type, dev->virt_name, cmd, + scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + res = EINVAL; + goto out; + } + + res = __scst_parse_vrprotect(cmd, 10); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * So far we support the only variable length CDBs, 32 bytes SBC PI type 2 + * commands, so 32 on the forth place is OK. Don't forget to change it + * adding in this group new commands with other lengths! + */ +static const int scst_cdb_length[8] = { 6, 10, 10, 32, 16, 12, 0, 0 }; #define SCST_CDB_GROUP(opcode) ((opcode >> 5) & 0x7) -#define SCST_GET_CDB_LEN(opcode) SCST_CDB_LENGTH[SCST_CDB_GROUP(opcode)] +#define SCST_GET_CDB_LEN(opcode) scst_cdb_length[SCST_CDB_GROUP(opcode)] static int get_cdb_info_len_10(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) @@ -7311,7 +9751,7 @@ static int get_cdb_info_verify10(struct scst_cmd *cmd, cmd->data_len = get_unaligned_be16(cmd->cdb + sdbops->info_len_off); cmd->data_direction = SCST_DATA_NONE; } - return 0; + return scst_parse_vrprotect(cmd); } static int get_cdb_info_verify6(struct scst_cmd *cmd, @@ -7367,7 +9807,7 @@ static int get_cdb_info_verify12(struct scst_cmd *cmd, cmd->data_len = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); cmd->data_direction = SCST_DATA_NONE; } - return 0; + return scst_parse_vrprotect(cmd); } static int get_cdb_info_verify16(struct scst_cmd *cmd, @@ -7397,7 +9837,37 @@ static int get_cdb_info_verify16(struct scst_cmd *cmd, cmd->data_len = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); cmd->data_direction = SCST_DATA_NONE; } - return 0; + return scst_parse_vrprotect(cmd); +} + +static int get_cdb_info_verify32(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + if (unlikely(cmd->cdb[10] & 4)) { + PRINT_ERROR("VERIFY(16): BYTCHK 1x not supported (dev %s)", + cmd->dev ? cmd->dev->virt_name : NULL); + scst_set_invalid_field_in_cdb(cmd, 1, + 2 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return 1; + } + + cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); + if (cmd->cdb[10] & 2) { + cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); + if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { + PRINT_ERROR("Too big bufflen %d (op %s)", + cmd->bufflen, scst_get_opcode_name(cmd)); + scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); + return 1; + } + cmd->data_len = cmd->bufflen; + cmd->data_direction = SCST_DATA_WRITE; + } else { + cmd->bufflen = 0; + cmd->data_len = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); + cmd->data_direction = SCST_DATA_NONE; + } + return scst_parse_vrprotect32(cmd); } static int get_cdb_info_len_1(struct scst_cmd *cmd, @@ -7411,7 +9881,7 @@ static int get_cdb_info_len_1(struct scst_cmd *cmd, return 0; } -static int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, +static inline int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { cmd->lba = (cmd->cdb[sdbops->info_lba_off] & 0x1F) << 16; @@ -7432,6 +9902,40 @@ static int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, return 0; } +static int get_cdb_info_lba_3_len_1_256_read(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_3_len_1_256(cmd, sdbops); + if (res != 0) + goto out; + else { + struct scst_device *dev = cmd->dev; + if ((dev->dev_dif_mode != SCST_DIF_MODE_NONE) && !dev->dpicz) + cmd->cmd_dif_actions = dev->dev_dif_rd_prot0_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + SCST_DIF_CHECK_REF_TAG; + } +out: + return res; +} + +static int get_cdb_info_lba_3_len_1_256_write(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_3_len_1_256(cmd, sdbops); + if (res != 0) + goto out; + else { + struct scst_device *dev = cmd->dev; + if (dev->dev_dif_mode != SCST_DIF_MODE_NONE) + cmd->cmd_dif_actions = dev->dev_dif_wr_prot0_actions | + SCST_DIF_CHECK_GUARD_TAG | dev->dif_app_chk | + SCST_DIF_CHECK_REF_TAG; + } +out: + return res; +} + static int get_cdb_info_len_2(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { @@ -7526,7 +10030,32 @@ static int get_cdb_info_lba_4_len_2(struct scst_cmd *cmd, return 0; } -static int get_cdb_info_lba_4_len_4(struct scst_cmd *cmd, +static int get_cdb_info_read_10(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_4_len_2(cmd, sdbops); + if (res != 0) + return res; + else { +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS + EXTRACHECKS_BUG_ON(cmd->cdb[0] != READ_10); + cmd->cmd_corrupt_dif_tag = (cmd->cdb[6] & 0xE0) >> 5; +#endif + return scst_parse_rdprotect(cmd); + } +} + +static int get_cdb_info_lba_4_len_2_wrprotect(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_4_len_2(cmd, sdbops); + if (res != 0) + return res; + else + return scst_parse_wrprotect(cmd); +} + +static inline int get_cdb_info_lba_4_len_4(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { cmd->lba = get_unaligned_be32(cmd->cdb + sdbops->info_lba_off); @@ -7541,7 +10070,27 @@ static int get_cdb_info_lba_4_len_4(struct scst_cmd *cmd, return 0; } -static int get_cdb_info_lba_8_len_4(struct scst_cmd *cmd, +static int get_cdb_info_lba_4_len_4_rdprotect(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_4_len_4(cmd, sdbops); + if (res != 0) + return res; + else + return scst_parse_rdprotect(cmd); +} + +static int get_cdb_info_lba_4_len_4_wrprotect(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_4_len_4(cmd, sdbops); + if (res != 0) + return res; + else + return scst_parse_wrprotect(cmd); +} + +static inline int get_cdb_info_lba_8_len_4(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); @@ -7556,13 +10105,58 @@ static int get_cdb_info_lba_8_len_4(struct scst_cmd *cmd, return 0; } +static int get_cdb_info_read_16(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_8_len_4(cmd, sdbops); + if (res != 0) + return res; + else { +#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS + EXTRACHECKS_BUG_ON(cmd->cdb[0] != READ_16); + cmd->cmd_corrupt_dif_tag = (cmd->cdb[14] & 0xE0) >> 5; +#endif + return scst_parse_rdprotect(cmd); + } +} + +static int get_cdb_info_lba_8_len_4_wrprotect(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_8_len_4(cmd, sdbops); + if (res != 0) + return res; + else + return scst_parse_wrprotect(cmd); +} + +static int get_cdb_info_lba_8_len_4_wrprotect32(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_8_len_4(cmd, sdbops); + if (res != 0) + return res; + else + return scst_parse_wrprotect32(cmd); +} + +static int get_cdb_info_lba_8_len_4_rdprotect32(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res = get_cdb_info_lba_8_len_4(cmd, sdbops); + if (res != 0) + return res; + else + return scst_parse_rdprotect32(cmd); +} + static int get_cdb_info_write_same10(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { cmd->lba = get_unaligned_be32(cmd->cdb + sdbops->info_lba_off); cmd->bufflen = 1; cmd->data_len = get_unaligned_be16(cmd->cdb + sdbops->info_len_off); - return 0; + return scst_parse_wrprotect(cmd); } static int get_cdb_info_write_same16(struct scst_cmd *cmd, @@ -7571,7 +10165,132 @@ static int get_cdb_info_write_same16(struct scst_cmd *cmd, cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); cmd->bufflen = 1; cmd->data_len = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); - return 0; + return scst_parse_wrprotect(cmd); +} + +static int get_cdb_info_write_same32(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); + cmd->bufflen = 1; + cmd->data_len = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); + return scst_parse_wrprotect32(cmd); +} + +static int scst_set_cmd_from_cdb_info(struct scst_cmd *cmd, + const struct scst_sdbops *ptr) +{ + cmd->cdb_len = SCST_GET_CDB_LEN(cmd->cdb[0]); + cmd->cmd_naca = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT); + cmd->cmd_linked = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_LINK_BIT); + cmd->op_name = ptr->info_op_name; + cmd->data_direction = ptr->info_data_direction; + cmd->op_flags = ptr->info_op_flags | SCST_INFO_VALID; + cmd->lba_off = ptr->info_lba_off; + cmd->lba_len = ptr->info_lba_len; + cmd->len_off = ptr->info_len_off; + cmd->len_len = ptr->info_len_len; + return (*ptr->get_cdb_info)(cmd, ptr); +} + +static int get_cdb_info_var_len(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + int res; + /* + * !! Indexed by (cdb[8-9] - SUBCODE_READ_32), the smallest subcode, + * !! hence all items in the array MUST be sorted and with NO HOLES + * !! in ops field! + */ + static const struct scst_sdbops scst_scsi_op32_table[] = { + {.ops = 0x7F, .devkey = "O ", + .info_op_name = "READ(32)", + .info_data_direction = SCST_DATA_READ, + .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_EXCL_ALLOWED, + .info_lba_off = 12, .info_lba_len = 8, + .info_len_off = 28, .info_len_len = 4, + .get_cdb_info = get_cdb_info_lba_8_len_4_rdprotect32}, + {.ops = 0x7F, .devkey = "O ", + .info_op_name = "VERIFY(32)", + .info_data_direction = SCST_DATA_UNKNOWN, + .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_EXCL_ALLOWED, + .info_lba_off = 12, .info_lba_len = 8, + .info_len_off = 28, .info_len_len = 4, + .get_cdb_info = get_cdb_info_verify32}, + {.ops = 0x7F, .devkey = "O ", + .info_op_name = "WRITE(32)", + .info_data_direction = SCST_DATA_WRITE, + .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_MEDIUM, + .info_lba_off = 12, .info_lba_len = 8, + .info_len_off = 28, .info_len_len = 4, + .get_cdb_info = get_cdb_info_lba_8_len_4_wrprotect32}, + {.ops = 0x7F, .devkey = "O ", + .info_op_name = "WRITE AND VERIFY(32)", + .info_data_direction = SCST_DATA_WRITE, + .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, + .info_lba_off = 12, .info_lba_len = 8, + .info_len_off = 28, .info_len_len = 4, + .get_cdb_info = get_cdb_info_lba_8_len_4_wrprotect32}, + {.ops = 0x7F, .devkey = "O ", + .info_op_name = "WRITE SAME(32)", + .info_data_direction = SCST_DATA_WRITE, + .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, + .info_lba_off = 12, .info_lba_len = 8, + .info_len_off = 28, .info_len_len = 4, + .get_cdb_info = get_cdb_info_write_same32}, + }; + const struct scst_sdbops *ptr; + int subcode = be16_to_cpu(*(__be16 *)&cmd->cdb[8]); + unsigned int i = subcode - SUBCODE_READ_32; + + EXTRACHECKS_BUG_ON(cmd->cdb[0] != 0x7F); + EXTRACHECKS_BUG_ON(sdbops->ops != 0x7F); + + /* i is unsigned, so this will handle subcodes < READ_32 as well */ + if (unlikely(i >= ARRAY_SIZE(scst_scsi_op32_table))) { + TRACE(TRACE_MINOR, "Too big cmd index %d for 0x7F CDB (cmd %p)", + i, cmd); + goto out_unknown; + } + + ptr = &scst_scsi_op32_table[i]; +#if 0 /* not possible */ + if (unlikely(ptr == NULL)) + goto out_unknown; +#endif + + if (unlikely(cmd->cdb[7] != 0x18)) { + TRACE(TRACE_MINOR, "Incorrect ADDITIONAL CDB LENGTH %d for " + "0x7F CDB (cmd %p)", cmd->cdb[7], cmd); + cmd->op_flags |= SCST_LBA_NOT_VALID; + scst_set_invalid_field_in_cdb(cmd, 7, 0); + res = 1; /* command invalid */ + goto out; + } + + res = scst_set_cmd_from_cdb_info(cmd, ptr); + + cmd->cmd_naca = (cmd->cdb[1] & CONTROL_BYTE_NACA_BIT); + cmd->cmd_linked = (cmd->cdb[1] & CONTROL_BYTE_LINK_BIT); + +out: + TRACE_EXIT_RES(res); + return res; + +out_unknown: + TRACE(TRACE_MINOR, "Unknown opcode 0x%x, subcode %d for type %d", + cmd->cdb[0], subcode, cmd->dev->type); + cmd->op_flags &= ~SCST_INFO_VALID; + res = -1; /* command unknown */ + goto out; } static int get_cdb_info_compare_and_write(struct scst_cmd *cmd, @@ -7580,7 +10299,7 @@ static int get_cdb_info_compare_and_write(struct scst_cmd *cmd, cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); cmd->data_len = cmd->cdb[sdbops->info_len_off]; cmd->bufflen = 2 * cmd->data_len; - return 0; + return scst_parse_wrprotect(cmd); } /** @@ -7769,17 +10488,7 @@ int scst_get_cdb_info(struct scst_cmd *cmd) goto out; } - cmd->cdb_len = SCST_GET_CDB_LEN(op); - cmd->cmd_naca = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT); - cmd->cmd_linked = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_LINK_BIT); - cmd->op_name = ptr->info_op_name; - cmd->data_direction = ptr->info_data_direction; - cmd->op_flags = ptr->info_op_flags | SCST_INFO_VALID; - cmd->lba_off = ptr->info_lba_off; - cmd->lba_len = ptr->info_lba_len; - cmd->len_off = ptr->info_len_off; - cmd->len_len = ptr->info_len_len; - res = (*ptr->get_cdb_info)(cmd, ptr); + res = scst_set_cmd_from_cdb_info(cmd, ptr); out: TRACE_EXIT_RES(res); @@ -8234,7 +10943,6 @@ int scst_block_generic_dev_done(struct scst_cmd *cmd, void (*set_block_shift)(struct scst_cmd *cmd, int block_shift)) { int opcode = cmd->cdb[0]; - int status = cmd->status; int res = SCST_CMD_STATE_DEFAULT; TRACE_ENTRY(); @@ -8246,7 +10954,7 @@ int scst_block_generic_dev_done(struct scst_cmd *cmd, */ if (unlikely(opcode == READ_CAPACITY)) { - if ((status == SAM_STAT_GOOD) || (status == SAM_STAT_CONDITION_MET)) { + if (scst_cmd_completed_good(cmd)) { /* Always keep track of disk capacity */ int buffer_size, sector_size, sh; uint8_t *buffer; @@ -8304,7 +11012,7 @@ int scst_tape_generic_dev_done(struct scst_cmd *cmd, * therefore change them only if necessary */ - if (unlikely(cmd->status != SAM_STAT_GOOD)) + if (unlikely(!scst_cmd_completed_good(cmd))) goto out; switch (opcode) { @@ -9301,6 +12009,7 @@ int scst_obtain_device_parameters(struct scst_device *dev, dev->tst = buffer[4+2] >> 5; dev->tmf_only = (buffer[4+2] & 0x10) >> 4; + dev->dpicz = (buffer[4+2] & 0x8) >> 3; q = buffer[4+3] >> 4; if (q > SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER) { PRINT_ERROR("Too big QUEUE ALG %x, dev %s, " @@ -9323,11 +12032,11 @@ int scst_obtain_device_parameters(struct scst_device *dev, dev->has_own_order_mgmt = !dev->queue_alg; PRINT_INFO("Device %s: TST %x, TMF_ONLY %x, QUEUE ALG %x, " - "QErr %x, SWP %x, TAS %x, D_SENSE %d, " + "QErr %x, SWP %x, TAS %x, D_SENSE %d, DPICZ %d, " "has_own_order_mgmt %d", dev->virt_name, dev->tst, dev->tmf_only, dev->queue_alg, dev->qerr, dev->swp, dev->tas, dev->d_sense, - dev->has_own_order_mgmt); + dev->dpicz, dev->has_own_order_mgmt); goto out; } else { @@ -9386,10 +12095,10 @@ int scst_obtain_device_parameters(struct scst_device *dev, brk: PRINT_WARNING("Unable to get device's %s control mode page, using " "existing values/defaults: TST %x, TMF_ONLY %x, QUEUE ALG %x, " - "QErr %x, SWP %x, TAS %x, D_SENSE %d, has_own_order_mgmt %d", - dev->virt_name, dev->tst, dev->tmf_only, dev->queue_alg, - dev->qerr, dev->swp, dev->tas, dev->d_sense, - dev->has_own_order_mgmt); + "QErr %x, SWP %x, TAS %x, D_SENSE %d, DPICZ %d, " + "has_own_order_mgmt %d", dev->virt_name, dev->tst, + dev->tmf_only, dev->queue_alg, dev->qerr, dev->swp, dev->tas, + dev->d_sense, dev->dpicz, dev->has_own_order_mgmt); out: TRACE_EXIT(); @@ -9983,6 +12692,7 @@ static void scst_free_descriptors(struct scst_cmd *cmd) #define SCST_SWP_LABEL "SWP" #define SCST_DSENSE_LABEL "D_SENSE" #define SCST_QUEUE_ALG_LABEL "QUEUE_ALG" +#define SCST_DPICZ_LABEL "DPICZ" int scst_save_global_mode_pages(const struct scst_device *dev, uint8_t *buf, int size) @@ -10026,6 +12736,13 @@ int scst_save_global_mode_pages(const struct scst_device *dev, goto out_overflow; } + if (dev->dpicz != dev->dpicz_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_DPICZ_LABEL, dev->dpicz); + if (res >= size-1) + goto out_overflow; + } + if (dev->queue_alg != dev->queue_alg_default) { res += scnprintf(&buf[res], size - res, "%s=%d\n", SCST_QUEUE_ALG_LABEL, dev->queue_alg); @@ -10175,6 +12892,32 @@ out: return res; } +static int scst_restore_dpicz(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_DPICZ_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->dpicz = val; + dev->dpicz_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_DPICZ_LABEL, + dev->dpicz, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + static int scst_restore_queue_alg(struct scst_device *dev, unsigned int val) { int res; @@ -10246,6 +12989,8 @@ int scst_restore_global_mode_pages(struct scst_device *dev, char *params, res = scst_restore_swp(dev, val); else if (strcasecmp(SCST_DSENSE_LABEL, p) == 0) res = scst_restore_dsense(dev, val); + else if (strcasecmp(SCST_DPICZ_LABEL, p) == 0) + res = scst_restore_dpicz(dev, val); else if (strcasecmp(SCST_QUEUE_ALG_LABEL, p) == 0) res = scst_restore_queue_alg(dev, val); else { @@ -10328,8 +13073,7 @@ int scst_vfs_fsync(struct file *file, loff_t loff, loff_t len) { int res; - res = sync_page_range(file->f_dentry->d_inode, file->f_mapping, - loff, len); + res = sync_page_range(file_inode(file), file->f_mapping, loff, len); return res; } #endif @@ -10371,7 +13115,7 @@ int scst_copy_file(const char *src, const char *dest) goto out_close; } - inode = file_src->f_dentry->d_inode; + inode = file_inode(file_src); if (S_ISREG(inode->i_mode)) /* Nothing to do */; @@ -10588,7 +13332,7 @@ static int __scst_read_file_transactional(const char *file_name, goto out; } - inode = file->f_dentry->d_inode; + inode = file_inode(file); if (S_ISREG(inode->i_mode)) /* Nothing to do */; @@ -10673,7 +13417,7 @@ int scst_get_file_mode(const char *path) res = PTR_ERR(file); goto out; } - res = file->f_dentry->d_inode->i_mode; + res = file_inode(file)->i_mode; filp_close(file, NULL); out: diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 6c40d22ea..63e7b7ab8 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1,9 +1,9 @@ /* * scst_main.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -231,12 +231,9 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, goto out; } - if (!vtt->detect) { - PRINT_ERROR("Target driver %s must have " - "detect() method.", vtt->name); - res = -EINVAL; - goto out; - } + if (vtt->detect) + PRINT_WARNING("detect() method is obsolete and scheduled for " + "removal (target driver %s)", vtt->name); if (!vtt->release) { PRINT_ERROR("Target driver %s must have " @@ -316,7 +313,7 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, mutex_unlock(&scst_mutex); TRACE_DBG("%s", "Calling target driver's detect()"); - res = vtt->detect(vtt); + res = vtt->detect ? vtt->detect(vtt) : 0; TRACE_DBG("Target driver's detect() returned %d", res); if (res < 0) { PRINT_ERROR("%s", "The detect() routine failed"); @@ -724,13 +721,14 @@ static const char *const scst_cmd_state_name[] = { [SCST_CMD_STATE_XMIT_WAIT] = "XMIT_WAIT", }; -static void scst_get_cmd_state_name(char *name, int len, unsigned state) +char *scst_get_cmd_state_name(char *name, int len, unsigned state) { if (state < ARRAY_SIZE(scst_cmd_state_name) && scst_cmd_state_name[state]) strlcpy(name, scst_cmd_state_name[state], len); else snprintf(name, len, "%d", state); + return name; } static char *scst_dump_cdb(char *buf, int buf_len, struct scst_cmd *cmd) @@ -767,11 +765,14 @@ void scst_trace_cmds(scst_show_fn show, void *arg) scst_get_cmd_state_name(state_name, sizeof(state_name), cmd->state); - show(arg, "cmd %p: state %s; tgtt %s; " + show(arg, "cmd %p: state %s; op %s; " + "proc time %ld sec; tgtt %s; " "tgt %s; session %s; grp %s; " "LUN %lld; ini %s; cdb %s\n", - cmd, state_name, t->name, - tgt->tgt_name, sess->sess_name, + cmd, state_name, + scst_get_opcode_name(cmd), + (long)(jiffies - cmd->start_time) / HZ, + t->name, tgt->tgt_name, sess->sess_name, tgt_dev ? (tgt_dev->acg_dev->acg->acg_name ? : "(default)") : "?", cmd->lun, sess->initiator_name, cdb); @@ -799,13 +800,13 @@ static const char *const scst_tm_fn_name[] = { [SCST_PR_ABORT_ALL] = "PR_ABORT_ALL", }; -static void scst_get_tm_fn_name(char *name, int len, unsigned fn) +char *scst_get_tm_fn_name(char *name, int len, unsigned fn) { if (fn < ARRAY_SIZE(scst_tm_fn_name) && scst_tm_fn_name[fn]) strlcpy(name, scst_tm_fn_name[fn], len); else snprintf(name, len, "%d", fn); - return; + return name; } static const char *const scst_mcmd_state_name[] = { @@ -818,14 +819,14 @@ static const char *const scst_mcmd_state_name[] = { [SCST_MCMD_STATE_FINISHED] = "FINISHED", }; -static void scst_get_mcmd_state_name(char *name, int len, unsigned state) +char *scst_get_mcmd_state_name(char *name, int len, unsigned state) { if (state < ARRAY_SIZE(scst_mcmd_state_name) && scst_mcmd_state_name[state]) strlcpy(name, scst_mcmd_state_name[state], len); else snprintf(name, len, "%d", state); - return; + return name; } void scst_trace_mcmds(scst_show_fn show, void *arg) @@ -897,6 +898,8 @@ static int scst_susp_wait(unsigned long timeout) goto out; if (res == 0) { + PRINT_INFO("%d active commands to still not completed. See " + "README for possible reasons.", scst_get_cmd_counter()); scst_trace_cmds(scst_to_syslog, &hp); scst_trace_mcmds(scst_to_syslog, &hp); } @@ -978,15 +981,8 @@ int scst_suspend_activity(unsigned long timeout) */ if (scst_get_cmd_counter() != 0) { - PRINT_INFO("Waiting for %d active commands to complete... This " - "might take few minutes for disks or few hours for " - "tapes, if you use long executed commands, like " - "REWIND or FORMAT. In case, if you have a hung user " - "space device (i.e. made using scst_user module) not " - "responding to any commands, if might take virtually " - "forever until the corresponding user space " - "program recovers and starts responding or gets " - "killed.", scst_get_cmd_counter()); + PRINT_INFO("Waiting for %d active commands to complete...", + scst_get_cmd_counter()); rep = true; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) @@ -1206,6 +1202,7 @@ out_del_unlocked: mutex_lock(&scst_mutex); list_del_init(&dev->dev_list_entry); mutex_unlock(&scst_mutex); + scst_free_device(dev); goto out; #else diff --git a/scst/src/scst_mem.c b/scst/src/scst_mem.c index b3ab75e1a..6ccb8cf40 100644 --- a/scst/src/scst_mem.c +++ b/scst/src/scst_mem.c @@ -1,8 +1,8 @@ /* * scst_mem.c * - * Copyright (C) 2006 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2006 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_mem.h b/scst/src/scst_mem.h index 68e33a092..e36153e64 100644 --- a/scst/src/scst_mem.h +++ b/scst/src/scst_mem.h @@ -1,8 +1,8 @@ /* * scst_mem.h * - * Copyright (C) 2006 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2006 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_module.c b/scst/src/scst_module.c index 53a8a0263..da5a29fda 100644 --- a/scst/src/scst_module.c +++ b/scst/src/scst_module.c @@ -1,9 +1,9 @@ /* * scst_module.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Support for loading target modules. The usage is similar to scsi_module.c * diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 2f5ecbdbe..282939f45 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -3,7 +3,7 @@ * * Copyright (C) 2009 - 2010 Alexey Obitotskiy * Copyright (C) 2009 - 2010 Open-E, Inc. - * Copyright (C) 2009 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2009 - 2015 Vladislav Bolkhovitin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -682,7 +682,7 @@ static int scst_pr_do_load_device_file(struct scst_device *dev, goto out; } - inode = file->f_dentry->d_inode; + inode = file_inode(file); if (S_ISREG(inode->i_mode)) /* Nothing to do */; @@ -1535,6 +1535,11 @@ static int scst_pr_register_all_tg_pt(struct scst_cmd *cmd, uint8_t *buffer, continue; if (tgt->rel_tgt_id == 0) continue; + if (tgt->tgt_forwarding) { + TRACE_PR("ALL_TG_PT: skipping forwarding " + "target %s", tgt->tgt_name); + continue; + } TRACE_PR("tgt %s, rel_tgt_id %d", tgt->tgt_name, tgt->rel_tgt_id); res = scst_pr_register_on_tgt_id(cmd, tgt->rel_tgt_id, diff --git a/scst/src/scst_pres.h b/scst/src/scst_pres.h index 98b8f6f33..3cbecacc5 100644 --- a/scst/src/scst_pres.h +++ b/scst/src/scst_pres.h @@ -3,7 +3,7 @@ * * Copyright (C) 2009 - 2010 Alexey Obitotskiy * Copyright (C) 2009 - 2010 Open-E, Inc. - * Copyright (C) 2009 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2009 - 2015 Vladislav Bolkhovitin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index fce62d9af..5248280db 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -1,9 +1,9 @@ /* * scst_priv.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -274,6 +274,10 @@ extern void scst_tgt_dev_stop_threads(struct scst_tgt_dev *tgt_dev); extern struct scst_dev_type scst_null_devtype; +char *scst_get_cmd_state_name(char *name, int len, unsigned state); +char *scst_get_mcmd_state_name(char *name, int len, unsigned state); +char *scst_get_tm_fn_name(char *name, int len, unsigned fn); + extern struct scst_cmd *__scst_check_deferred_commands_locked( struct scst_order_data *order_data, bool return_first); extern struct scst_cmd *__scst_check_deferred_commands( @@ -520,6 +524,11 @@ static inline void scst_devt_dev_sysfs_del(struct scst_device *dev) { } static inline void scst_dev_sysfs_del(struct scst_device *dev) { } +static inline int scst_dev_sysfs_dif_create(struct scst_device *dev) +{ + return 0; +} + static inline int scst_tgt_dev_sysfs_create(struct scst_tgt_dev *tgt_dev) { return 0; @@ -568,6 +577,7 @@ int scst_devt_sysfs_create(struct scst_dev_type *devt); void scst_devt_sysfs_del(struct scst_dev_type *devt); int scst_dev_sysfs_create(struct scst_device *dev); void scst_dev_sysfs_del(struct scst_device *dev); +int scst_dev_sysfs_dif_create(struct scst_device *dev); int scst_tgt_dev_sysfs_create(struct scst_tgt_dev *tgt_dev); void scst_tgt_dev_sysfs_del(struct scst_tgt_dev *tgt_dev); int scst_devt_dev_sysfs_create(struct scst_device *dev); diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index e1b458bcb..e430c1a36 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -1,9 +1,9 @@ /* * scst_proc.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -269,7 +269,7 @@ int scst_proc_log_entry_write(struct file *file, const char __user *buf, unsigned long level = 0, oldlevel; char *buffer, *p, *e; const struct scst_trace_log *t; - char *data = PDE_DATA(file->f_dentry->d_inode); + char *data = PDE_DATA(file_inode(file)); TRACE_ENTRY(); @@ -1390,7 +1390,7 @@ static ssize_t scst_proc_scsi_tgt_write(struct file *file, const char __user *buf, size_t length, loff_t *off) { - struct scst_tgt *vtt = PDE_DATA(file->f_dentry->d_inode); + struct scst_tgt *vtt = PDE_DATA(file_inode(file)); ssize_t res = 0; char *buffer; char *start; @@ -1545,7 +1545,7 @@ static ssize_t scst_proc_scsi_dev_handler_write(struct file *file, const char __user *buf, size_t length, loff_t *off) { - struct scst_dev_type *dev_type = PDE_DATA(file->f_dentry->d_inode); + struct scst_dev_type *dev_type = PDE_DATA(file_inode(file)); ssize_t res = 0; char *buffer; char *start; @@ -1913,7 +1913,7 @@ static ssize_t scst_proc_groups_devices_write(struct file *file, int res, action, rc, read_only = 0; char *buffer, *p, *e = NULL; unsigned int virt_lun; - struct scst_acg *acg = PDE_DATA(file->f_dentry->d_inode); + struct scst_acg *acg = PDE_DATA(file_inode(file)); struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp; struct scst_device *d, *dev = NULL; @@ -2150,7 +2150,7 @@ static ssize_t scst_proc_groups_names_write(struct file *file, { int res = length, rc = 0, action; char *buffer, *p, *pp = NULL; - struct scst_acg *acg = PDE_DATA(file->f_dentry->d_inode); + struct scst_acg *acg = PDE_DATA(file_inode(file)); struct scst_acn *n, *nn; TRACE_ENTRY(); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 71be58f89..29ca48730 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -2,8 +2,8 @@ * scst_sysfs.c * * Copyright (C) 2009 Daniel Henrique Debonzi - * Copyright (C) 2009 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2009 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1033,6 +1033,63 @@ static struct kobj_attribute scst_tgtt_mgmt = __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_tgtt_mgmt_show, scst_tgtt_mgmt_store); +static ssize_t scst_tgtt_dif_capable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_tgt_template *tgtt; + + TRACE_ENTRY(); + + tgtt = container_of(kobj, struct scst_tgt_template, tgtt_kobj); + + EXTRACHECKS_BUG_ON(!tgtt->dif_supported); + + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "dif_supported"); + + if (tgtt->hw_dif_type1_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_type1_supported"); + + if (tgtt->hw_dif_type2_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_type2_supported"); + + if (tgtt->hw_dif_type3_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_type3_supported"); + + if (tgtt->hw_dif_ip_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_ip_supported"); + + if (tgtt->hw_dif_same_sg_layout_required) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_same_sg_layout_required"); + + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, "\n"); + + if (tgtt->supported_dif_block_sizes) { + const int *p = tgtt->supported_dif_block_sizes; + int j; + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "Supported blocks: "); + j = pos; + while (*p != 0) { + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "%s%d", (j == pos) ? "" : ", ", *p); + p++; + } + } + + TRACE_EXIT_RES(pos); + return pos; +} + +static struct kobj_attribute scst_tgtt_dif_capable_attr = + __ATTR(dif_capabilities, S_IRUGO, scst_tgtt_dif_capable_show, NULL); + /* * Creates an attribute entry for target driver. */ @@ -1085,6 +1142,16 @@ int scst_tgtt_sysfs_create(struct scst_tgt_template *tgtt) } } + if (tgtt->dif_supported) { + res = sysfs_create_file(&tgtt->tgtt_kobj, + &scst_tgtt_dif_capable_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't add attribute %s for target driver %s", + scst_tgtt_dif_capable_attr.attr.name, tgtt->name); + goto out; + } + } + #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) if (tgtt->trace_flags != NULL) { res = sysfs_create_file(&tgtt->tgtt_kobj, @@ -1884,9 +1951,12 @@ static ssize_t __scst_acg_cpu_mask_show(struct scst_acg *acg, char *buf) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) res = cpumask_scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, acg->acg_cpu_mask); -#else +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) res = cpumask_scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, &acg->acg_cpu_mask); +#else + res = scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, "%*pb", + cpumask_pr_args(&acg->acg_cpu_mask)); #endif if (!cpumask_equal(&acg->acg_cpu_mask, &default_cpu_mask)) res += sprintf(&buf[res], "\n%s\n", SCST_SYSFS_KEY_MARK); @@ -2428,6 +2498,95 @@ static struct kobj_attribute scst_rel_tgt_id = __ATTR(rel_tgt_id, S_IRUGO | S_IWUSR, scst_rel_tgt_id_show, scst_rel_tgt_id_store); +static ssize_t scst_tgt_forwarding_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_tgt *tgt; + int res; + + TRACE_ENTRY(); + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + res = sprintf(buf, "%d\n%s", tgt->tgt_forwarding, + tgt->tgt_forwarding ? SCST_SYSFS_KEY_MARK "\n" : ""); + + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t scst_tgt_forwarding_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res = 0; + struct scst_tgt *tgt; + struct scst_session *sess; + int old; + + TRACE_ENTRY(); + + if ((buf == NULL) || (count == 0)) { + res = 0; + goto out; + } + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + mutex_lock(&scst_mutex); + + old = tgt->tgt_forwarding; + + switch (buf[0]) { + case '0': + tgt->tgt_forwarding = 0; + break; + case '1': + tgt->tgt_forwarding = 1; + break; + default: + PRINT_ERROR("%s: Requested action not understood: %s", + __func__, buf); + res = -EINVAL; + goto out_unlock; + } + + if (tgt->tgt_forwarding == old) + goto out_unlock; + + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + int i; + for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { + struct list_head *head = &sess->sess_tgt_dev_list[i]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + if (tgt->tgt_forwarding) + set_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags); + else + clear_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags); + } + } + } + + if (tgt->tgt_forwarding) + PRINT_INFO("Set target %s as forwarding", tgt->tgt_name); + else + PRINT_INFO("Clear target %s as forwarding", tgt->tgt_name); + +out_unlock: + mutex_unlock(&scst_mutex); + + if (res == 0) + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static struct kobj_attribute scst_tgt_forwarding = + __ATTR(forwarding, S_IRUGO | S_IWUSR, scst_tgt_forwarding_show, + scst_tgt_forwarding_store); + static ssize_t scst_tgt_comment_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -2521,6 +2680,114 @@ out: } EXPORT_SYMBOL(scst_create_tgt_attr); +static ssize_t scst_tgt_dif_capable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_tgt *tgt; + + TRACE_ENTRY(); + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + EXTRACHECKS_BUG_ON(!tgt->tgt_dif_supported); + + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "dif_supported"); + + if (tgt->tgt_hw_dif_type1_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_type1_supported"); + + if (tgt->tgt_hw_dif_type2_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_type2_supported"); + + if (tgt->tgt_hw_dif_type3_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_type3_supported"); + + if (tgt->tgt_hw_dif_ip_supported) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_ip_supported"); + + if (tgt->tgt_hw_dif_same_sg_layout_required) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + ", hw_dif_same_sg_layout_required"); + + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, "\n"); + + if (tgt->tgt_supported_dif_block_sizes) { + const int *p = tgt->tgt_supported_dif_block_sizes; + int j; + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "Supported blocks: "); + j = pos; + while (*p != 0) { + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "%s%d", (j == pos) ? "" : ", ", *p); + p++; + } + } + + TRACE_EXIT_RES(pos); + return pos; +} + +static struct kobj_attribute scst_tgt_dif_capable_attr = + __ATTR(dif_capabilities, S_IRUGO, scst_tgt_dif_capable_show, NULL); + +static ssize_t scst_tgt_dif_checks_failed_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_tgt *tgt; + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + pos = scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, "\tapp\tref\tguard\n" + "tgt\t%d\t%d\t%d\nscst\t%d\t%d\t%d\ndev\t%d\t%d\t%d\n", + atomic_read(&tgt->tgt_dif_app_failed_tgt), + atomic_read(&tgt->tgt_dif_ref_failed_tgt), + atomic_read(&tgt->tgt_dif_guard_failed_tgt), + atomic_read(&tgt->tgt_dif_app_failed_scst), + atomic_read(&tgt->tgt_dif_ref_failed_scst), + atomic_read(&tgt->tgt_dif_guard_failed_scst), + atomic_read(&tgt->tgt_dif_app_failed_dev), + atomic_read(&tgt->tgt_dif_ref_failed_dev), + atomic_read(&tgt->tgt_dif_guard_failed_dev)); + + return pos; +} + +static ssize_t scst_tgt_dif_checks_failed_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct scst_tgt *tgt; + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + PRINT_INFO("Zeroing DIF failures statistics for target %s", + tgt->tgt_name); + + atomic_set(&tgt->tgt_dif_app_failed_tgt, 0); + atomic_set(&tgt->tgt_dif_ref_failed_tgt, 0); + atomic_set(&tgt->tgt_dif_guard_failed_tgt, 0); + atomic_set(&tgt->tgt_dif_app_failed_scst, 0); + atomic_set(&tgt->tgt_dif_ref_failed_scst, 0); + atomic_set(&tgt->tgt_dif_guard_failed_scst, 0); + atomic_set(&tgt->tgt_dif_app_failed_dev, 0); + atomic_set(&tgt->tgt_dif_ref_failed_dev, 0); + atomic_set(&tgt->tgt_dif_guard_failed_dev, 0); + + return count; +} + +static struct kobj_attribute scst_tgt_dif_checks_failed_attr = + __ATTR(dif_checks_failed, S_IRUGO | S_IWUSR, + scst_tgt_dif_checks_failed_show, + scst_tgt_dif_checks_failed_store); + #define SCST_TGT_SYSFS_STAT_ATTR(member_name, attr, dir, result_op) \ static int scst_tgt_sysfs_##attr##_show_work_fn( \ struct scst_sysfs_work_item *work) \ @@ -2579,18 +2846,19 @@ static struct kobj_attribute scst_tgt_##attr##_attr = \ SCST_TGT_SYSFS_STAT_ATTR(cmd_count, unknown_cmd_count, SCST_DATA_UNKNOWN, >> 0); SCST_TGT_SYSFS_STAT_ATTR(cmd_count, write_cmd_count, SCST_DATA_WRITE, >> 0); -SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, write_io_count_kb, SCST_DATA_WRITE, - >> 10); +SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, write_io_count_kb, SCST_DATA_WRITE, >> 10); +SCST_TGT_SYSFS_STAT_ATTR(unaligned_cmd_count, write_unaligned_cmd_count, SCST_DATA_WRITE, >> 0); SCST_TGT_SYSFS_STAT_ATTR(cmd_count, read_cmd_count, SCST_DATA_READ, >> 0); -SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, read_io_count_kb, SCST_DATA_READ, - >> 10); +SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, read_io_count_kb, SCST_DATA_READ, >> 10); +SCST_TGT_SYSFS_STAT_ATTR(unaligned_cmd_count, read_unaligned_cmd_count, SCST_DATA_READ, >> 0); SCST_TGT_SYSFS_STAT_ATTR(cmd_count, bidi_cmd_count, SCST_DATA_BIDI, >> 0); -SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, bidi_io_count_kb, SCST_DATA_BIDI, - >> 10); +SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, bidi_io_count_kb, SCST_DATA_BIDI, >> 10); +SCST_TGT_SYSFS_STAT_ATTR(unaligned_cmd_count, bidi_unaligned_cmd_count, SCST_DATA_BIDI, >> 0); SCST_TGT_SYSFS_STAT_ATTR(cmd_count, none_cmd_count, SCST_DATA_NONE, >> 0); static struct attribute *scst_tgt_attrs[] = { &scst_rel_tgt_id.attr, + &scst_tgt_forwarding.attr, &scst_tgt_comment.attr, &scst_tgt_addr_method.attr, &scst_tgt_io_grouping_type.attr, @@ -2599,10 +2867,13 @@ static struct attribute *scst_tgt_attrs[] = { &scst_tgt_unknown_cmd_count_attr.attr, &scst_tgt_write_cmd_count_attr.attr, &scst_tgt_write_io_count_kb_attr.attr, + &scst_tgt_write_unaligned_cmd_count_attr.attr, &scst_tgt_read_cmd_count_attr.attr, &scst_tgt_read_io_count_kb_attr.attr, + &scst_tgt_read_unaligned_cmd_count_attr.attr, &scst_tgt_bidi_cmd_count_attr.attr, &scst_tgt_bidi_io_count_kb_attr.attr, + &scst_tgt_bidi_unaligned_cmd_count_attr.attr, &scst_tgt_none_cmd_count_attr.attr, NULL, }; @@ -2676,6 +2947,26 @@ int scst_tgt_sysfs_create(struct scst_tgt *tgt) goto out_err; } + if (tgt->tgt_dif_supported) { + res = sysfs_create_file(&tgt->tgt_kobj, + &scst_tgt_dif_capable_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't add attribute %s for tgt %s", + scst_tgt_dif_capable_attr.attr.name, + tgt->tgt_name); + goto out_err; + } + + res = sysfs_create_file(&tgt->tgt_kobj, + &scst_tgt_dif_checks_failed_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't add attribute %s for tgt %s", + scst_tgt_dif_checks_failed_attr.attr.name, + tgt->tgt_name); + goto out_err; + } + } + if (tgt->tgtt->tgt_attrs) { res = sysfs_create_files(&tgt->tgt_kobj, tgt->tgtt->tgt_attrs); if (res != 0) { @@ -3394,6 +3685,164 @@ void scst_dev_sysfs_del(struct scst_device *dev) return; } +static ssize_t scst_dev_dif_mode_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + + if (dev->dev_dif_mode == SCST_DIF_MODE_NONE) + pos = sprintf(buf, "None\n"); + else { + int j = pos; + + if (dev->dev_dif_mode & SCST_DIF_MODE_TGT) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "%s%s", (j == pos) ? "" : "|", SCST_DIF_MODE_TGT_STR); + + if (dev->dev_dif_mode & SCST_DIF_MODE_SCST) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "%s%s", (j == pos) ? "" : "|", SCST_DIF_MODE_SCST_STR); + + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_CHECK) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "%s%s", (j == pos) ? "" : "|", SCST_DIF_MODE_DEV_CHECK_STR); + + if (dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "%s%s", (j == pos) ? "" : "|", SCST_DIF_MODE_DEV_STORE_STR); + + pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos, + "\n%s", SCST_SYSFS_KEY_MARK "\n"); + } + + TRACE_EXIT_RES(pos); + return pos; +} + +static struct kobj_attribute scst_dev_dif_mode_attr = + __ATTR(dif_mode, S_IRUGO, scst_dev_dif_mode_show, NULL); + +static ssize_t scst_dev_dif_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + + pos = sprintf(buf, "%d\n%s", dev->dev_dif_type, + (dev->dev_dif_type != 0) ? SCST_SYSFS_KEY_MARK "\n" : ""); + + TRACE_EXIT_RES(pos); + return pos; +} + +static struct kobj_attribute scst_dev_dif_type_attr = + __ATTR(dif_type, S_IRUGO, scst_dev_dif_type_show, NULL); + +static ssize_t scst_dev_sysfs_dif_static_app_tag_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_device *dev; + unsigned long long val; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtoull(buf, 0, &val); +#else + res = strict_strtoull(buf, 0, &val); +#endif + if (res != 0) { + PRINT_ERROR("strtoul() for %s failed: %d (device %s)", + buf, res, dev->virt_name); + goto out; + } + + scst_dev_set_dif_static_app_tag_combined(dev, cpu_to_be64(val)); + + res = count; + + PRINT_INFO("APP TAG for device %s changed to %llx", dev->virt_name, + (long long)be64_to_cpu(scst_dev_get_dif_static_app_tag_combined(dev))); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t scst_dev_sysfs_dif_static_app_tag_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + __be64 a; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + + a = scst_dev_get_dif_static_app_tag_combined(dev); + + pos = sprintf(buf, "0x%llx\n%s", (unsigned long long)be64_to_cpu(a), + (a != SCST_DIF_NO_CHECK_APP_TAG) ? SCST_SYSFS_KEY_MARK "\n" : ""); + + TRACE_EXIT_RES(pos); + return pos; +} + +static struct kobj_attribute scst_dev_dif_static_app_tag_attr = + __ATTR(dif_static_app_tag, S_IWUSR|S_IRUGO, + scst_dev_sysfs_dif_static_app_tag_show, + scst_dev_sysfs_dif_static_app_tag_store); + +int scst_dev_sysfs_dif_create(struct scst_device *dev) +{ + int res; + + TRACE_ENTRY(); + + /* + * On errors the caller supposed to unregister this device, hence, + * perform the cleanup. + */ + + res = sysfs_create_file(&dev->dev_kobj, &scst_dev_dif_mode_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't create attr %s for dev %s", + scst_dev_dif_mode_attr.attr.name, dev->virt_name); + goto out; + } + + res = sysfs_create_file(&dev->dev_kobj, &scst_dev_dif_type_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't create attr %s for dev %s", + scst_dev_dif_type_attr.attr.name, dev->virt_name); + goto out; + } + + res = sysfs_create_file(&dev->dev_kobj, &scst_dev_dif_static_app_tag_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't create attr %s for dev %s", + scst_dev_dif_static_app_tag_attr.attr.name, dev->virt_name); + goto out; + } + +out: + TRACE_EXIT_RES(res); + return res; +} + /** ** Tgt_dev implementation **/ @@ -3551,6 +4000,58 @@ static struct kobj_attribute tgt_dev_active_commands_attr = __ATTR(active_commands, S_IRUGO, scst_tgt_dev_active_commands_show, NULL); +static ssize_t scst_tgt_dev_dif_checks_failed_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_tgt_dev *tgt_dev; + + tgt_dev = container_of(kobj, struct scst_tgt_dev, tgt_dev_kobj); + + pos = scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, "\tapp\tref\tguard\n" + "tgt\t%d\t%d\t%d\nscst\t%d\t%d\t%d\ndev\t%d\t%d\t%d\n", + atomic_read(&tgt_dev->tgt_dev_dif_app_failed_tgt), + atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_tgt), + atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_tgt), + atomic_read(&tgt_dev->tgt_dev_dif_app_failed_scst), + atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_scst), + atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_scst), + atomic_read(&tgt_dev->tgt_dev_dif_app_failed_dev), + atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_dev), + atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_dev)); + + return pos; +} + +static ssize_t scst_tgt_dev_dif_checks_failed_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct scst_tgt_dev *tgt_dev; + + tgt_dev = container_of(kobj, struct scst_tgt_dev, tgt_dev_kobj); + + PRINT_INFO("Zeroing DIF failures statistics for initiator " + "%s, target %s, LUN %lld", tgt_dev->sess->initiator_name, + tgt_dev->sess->tgt->tgt_name, (unsigned long long)tgt_dev->lun); + + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_dev, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_dev, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_dev, 0); + + return count; +} + +static struct kobj_attribute tgt_dev_dif_checks_failed_attr = + __ATTR(dif_checks_failed, S_IRUGO | S_IWUSR, + scst_tgt_dev_dif_checks_failed_show, + scst_tgt_dev_dif_checks_failed_store); + static struct attribute *scst_tgt_dev_attrs[] = { &tgt_dev_thread_pid_attr.attr, &tgt_dev_active_commands_attr.attr, @@ -3595,9 +4096,25 @@ int scst_tgt_dev_sysfs_create(struct scst_tgt_dev *tgt_dev) goto out; } + if (tgt_dev->sess->tgt->tgt_dif_supported && (tgt_dev->dev->dev_dif_type != 0)) { + res = sysfs_create_file(&tgt_dev->tgt_dev_kobj, + &tgt_dev_dif_checks_failed_attr.attr); + if (res != 0) { + PRINT_ERROR("Adding %s sysfs attribute to tgt_dev %lld " + "failed (%d)", tgt_dev_dif_checks_failed_attr.attr.name, + (unsigned long long)tgt_dev->lun, res); + goto out_del; + } + } + out: TRACE_EXIT_RES(res); return res; + +out_del: + kobject_del(&tgt_dev->tgt_dev_kobj); + kobject_put(&tgt_dev->tgt_dev_kobj); + goto out; } /* @@ -3941,6 +4458,161 @@ static struct kobj_attribute session_active_commands_attr = __ATTR(active_commands, S_IRUGO, scst_sess_sysfs_active_commands_show, NULL); +static int scst_sysfs_sess_get_dif_checks_failed_work_fn(struct scst_sysfs_work_item *work) +{ + int res, t; + struct scst_session *sess = work->sess; + int app_failed_tgt = 0, ref_failed_tgt = 0, guard_failed_tgt = 0; + int app_failed_scst = 0, ref_failed_scst = 0, guard_failed_scst = 0; + int app_failed_dev = 0, ref_failed_dev = 0, guard_failed_dev = 0; + + TRACE_ENTRY(); + + res = mutex_lock_interruptible(&scst_mutex); + if (res != 0) + goto out_put; + + for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) { + struct list_head *head = &sess->sess_tgt_dev_list[t]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + app_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_app_failed_tgt); + ref_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_tgt); + guard_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_tgt); + app_failed_scst += atomic_read(&tgt_dev->tgt_dev_dif_app_failed_scst); + ref_failed_scst += atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_scst); + guard_failed_scst += atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_scst); + app_failed_dev += atomic_read(&tgt_dev->tgt_dev_dif_app_failed_dev); + ref_failed_dev += atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_dev); + guard_failed_dev += atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_dev); + } + } + + mutex_unlock(&scst_mutex); + + work->res_buf = kasprintf(GFP_KERNEL, "\tapp\tref\tguard\n" + "tgt\t%d\t%d\t%d\nscst\t%d\t%d\t%d\ndev\t%d\t%d\t%d\n", + app_failed_tgt, ref_failed_tgt, guard_failed_tgt, + app_failed_scst, ref_failed_scst, guard_failed_scst, + app_failed_dev, ref_failed_dev, guard_failed_dev); + res = work->res_buf ? 0 : -ENOMEM; + +out_put: + kobject_put(&sess->sess_kobj); + + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t scst_sess_sysfs_dif_checks_failed_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int res; + struct scst_session *sess; + struct scst_sysfs_work_item *work; + + sess = container_of(kobj, struct scst_session, sess_kobj); + + res = scst_alloc_sysfs_work(scst_sysfs_sess_get_dif_checks_failed_work_fn, + true, &work); + if (res != 0) + goto out; + + work->sess = sess; + + SCST_SET_DEP_MAP(work, &scst_sess_dep_map); + kobject_get(&sess->sess_kobj); + + scst_sysfs_work_get(work); + + res = scst_sysfs_queue_wait_work(work); + if (res != 0) + goto out_put; + + res = snprintf(buf, SCST_SYSFS_BLOCK_SIZE, "%s", work->res_buf); + +out_put: + scst_sysfs_work_put(work); + +out: + return res; +} + +static int scst_sess_zero_dif_checks_failed(struct scst_sysfs_work_item *work) +{ + int res, t; + struct scst_session *sess = work->sess; + + TRACE_ENTRY(); + + PRINT_INFO("Zeroing DIF failures statistics for initiator " + "%s, target %s", sess->initiator_name, sess->tgt->tgt_name); + + res = mutex_lock_interruptible(&scst_mutex); + if (res != 0) + goto out_put; + + for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) { + struct list_head *head = &sess->sess_tgt_dev_list[t]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_tgt, 0); + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_scst, 0); + atomic_set(&tgt_dev->tgt_dev_dif_app_failed_dev, 0); + atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_dev, 0); + atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_dev, 0); + } + } + + mutex_unlock(&scst_mutex); + + res = 0; + +out_put: + kobject_put(&sess->sess_kobj); + + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t scst_sess_sysfs_dif_checks_failed_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_session *sess; + struct scst_sysfs_work_item *work; + + TRACE_ENTRY(); + + sess = container_of(kobj, struct scst_session, sess_kobj); + + res = scst_alloc_sysfs_work(scst_sess_zero_dif_checks_failed, false, &work); + if (res != 0) + goto out; + + work->sess = sess; + + SCST_SET_DEP_MAP(work, &scst_sess_dep_map); + kobject_get(&sess->sess_kobj); + + res = scst_sysfs_queue_wait_work(work); + if (res == 0) + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static struct kobj_attribute session_dif_checks_failed_attr = + __ATTR(dif_checks_failed, S_IRUGO | S_IWUSR, + scst_sess_sysfs_dif_checks_failed_show, + scst_sess_sysfs_dif_checks_failed_store); + static ssize_t scst_sess_sysfs_initiator_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -3989,6 +4661,7 @@ static ssize_t scst_sess_sysfs_##exported_name##_store(struct kobject *kobj, \ BUILD_BUG_ON(dir >= SCST_DATA_DIR_MAX); \ sess->io_stats[dir].cmd_count = 0; \ sess->io_stats[dir].io_byte_count = 0; \ + sess->io_stats[dir].unaligned_cmd_count = 0; \ spin_unlock_irq(&sess->sess_list_lock); \ return count; \ } \ @@ -4001,13 +4674,15 @@ static struct kobj_attribute session_##exported_name##_attr = \ SCST_SESS_SYSFS_STAT_ATTR(cmd_count, unknown_cmd_count, SCST_DATA_UNKNOWN, 0); SCST_SESS_SYSFS_STAT_ATTR(cmd_count, write_cmd_count, SCST_DATA_WRITE, 0); SCST_SESS_SYSFS_STAT_ATTR(io_byte_count, write_io_count_kb, SCST_DATA_WRITE, 1); +SCST_SESS_SYSFS_STAT_ATTR(unaligned_cmd_count, write_unaligned_cmd_count, SCST_DATA_WRITE, 0); SCST_SESS_SYSFS_STAT_ATTR(cmd_count, read_cmd_count, SCST_DATA_READ, 0); SCST_SESS_SYSFS_STAT_ATTR(io_byte_count, read_io_count_kb, SCST_DATA_READ, 1); +SCST_SESS_SYSFS_STAT_ATTR(unaligned_cmd_count, read_unaligned_cmd_count, SCST_DATA_READ, 0); SCST_SESS_SYSFS_STAT_ATTR(cmd_count, bidi_cmd_count, SCST_DATA_BIDI, 0); SCST_SESS_SYSFS_STAT_ATTR(io_byte_count, bidi_io_count_kb, SCST_DATA_BIDI, 1); +SCST_SESS_SYSFS_STAT_ATTR(unaligned_cmd_count, bidi_unaligned_cmd_count, SCST_DATA_BIDI, 0); SCST_SESS_SYSFS_STAT_ATTR(cmd_count, none_cmd_count, SCST_DATA_NONE, 0); - static ssize_t scst_sess_force_close_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -4036,10 +4711,13 @@ static struct attribute *scst_session_attrs[] = { &session_unknown_cmd_count_attr.attr, &session_write_cmd_count_attr.attr, &session_write_io_count_kb_attr.attr, + &session_write_unaligned_cmd_count_attr.attr, &session_read_cmd_count_attr.attr, &session_read_io_count_kb_attr.attr, + &session_read_unaligned_cmd_count_attr.attr, &session_bidi_cmd_count_attr.attr, &session_bidi_io_count_kb_attr.attr, + &session_bidi_unaligned_cmd_count_attr.attr, &session_none_cmd_count_attr.attr, #ifdef CONFIG_SCST_MEASURE_LATENCY &session_latency_attr.attr, @@ -4126,6 +4804,17 @@ int scst_sess_sysfs_create(struct scst_session *sess) } } + if (sess->tgt->tgt_dif_supported) { + res = sysfs_create_file(&sess->sess_kobj, + &session_dif_checks_failed_attr.attr); + if (res != 0) { + PRINT_ERROR("Adding %s sysfs attribute to session %s " + "failed (%d)", session_dif_checks_failed_attr.attr.name, + name, res); + goto out_del; + } + } + if (sess->tgt->tgtt->sess_attrs) { res = sysfs_create_files(&sess->sess_kobj, sess->tgt->tgtt->sess_attrs); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index d6a8fb6d0..be0029837 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -1,9 +1,9 @@ /* * scst_targ.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef INSIDE_KERNEL_TREE #include @@ -72,9 +73,13 @@ EXPORT_SYMBOL_GPL(scst_post_alloc_data_buf); static inline void scst_schedule_tasklet(struct scst_cmd *cmd) { - struct scst_percpu_info *i = &scst_percpu_infos[smp_processor_id()]; + struct scst_percpu_info *i; unsigned long flags; + preempt_disable(); + + i = &scst_percpu_infos[smp_processor_id()]; + if (atomic_read(&i->cpu_cmd_count) <= scst_max_tasklet_cmd) { spin_lock_irqsave(&i->tasklet_lock, flags); TRACE_DBG("Adding cmd %p to tasklet %d cmd list", cmd, @@ -92,6 +97,8 @@ static inline void scst_schedule_tasklet(struct scst_cmd *cmd) wake_up(&cmd->cmd_threads->cmd_list_waitQ); spin_unlock_irqrestore(&cmd->cmd_threads->cmd_list_lock, flags); } + + preempt_enable(); return; } @@ -577,12 +584,14 @@ int scst_pre_parse(struct scst_cmd *cmd) TRACE_DBG("op_name <%s> (cmd %p), direction=%d " "(expected %d, set %s), lba %lld, bufflen=%d, data_len %lld, " - "out_bufflen=%d (expected len %d, out expected len %d), " - "flags=0x%x, naca %d", cmd->op_name, cmd, cmd->data_direction, + "out_bufflen=%d (expected len data %d, expected len DIF %d, " + "out expected len %d), flags=0x%x, , naca %d", + cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", (long long)cmd->lba, cmd->bufflen, (long long)cmd->data_len, - cmd->out_bufflen, cmd->expected_transfer_len, + cmd->out_bufflen, scst_cmd_get_expected_transfer_len_data(cmd), + scst_cmd_get_expected_transfer_len_dif(cmd), cmd->expected_out_transfer_len, cmd->op_flags, cmd->cmd_naca); res = 0; @@ -616,6 +625,16 @@ static bool scst_is_allowed_to_mismatch_cmd(struct scst_cmd *cmd) } #endif +static bool scst_bufflen_eq_expecten_len(struct scst_cmd *cmd) +{ + int b = cmd->bufflen; + + if (cmd->tgt_dif_data_expected) + b += (b >> cmd->dev->block_shift) << SCST_DIF_TAG_SHIFT; + + return b == cmd->expected_transfer_len_full; +} + static int scst_parse_cmd(struct scst_cmd *cmd) { int res = SCST_CMD_STATE_RES_CONT_SAME; @@ -678,12 +697,12 @@ static int scst_parse_cmd(struct scst_cmd *cmd) #ifdef CONFIG_SCST_USE_EXPECTED_VALUES if (scst_cmd_is_expected_set(cmd)) { TRACE(TRACE_MINOR, "Using initiator supplied values: " - "direction %d, transfer_len %d/%d", - cmd->expected_data_direction, - cmd->expected_transfer_len, + "direction %d, transfer_len %d/%d/%d", + scst_cmd_get_expected_transfer_len_data(cmd), + scst_cmd_get_expected_transfer_len_dif(cmd), cmd->expected_out_transfer_len); cmd->data_direction = cmd->expected_data_direction; - cmd->bufflen = cmd->expected_transfer_len; + cmd->bufflen = scst_cmd_get_expected_transfer_len_data(cmd); cmd->data_len = cmd->bufflen; cmd->out_bufflen = cmd->expected_out_transfer_len; } else { @@ -721,7 +740,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) * expected value, but limit it to some * reasonable value (15MB). */ - cmd->bufflen = min(cmd->expected_transfer_len, + cmd->bufflen = min(scst_cmd_get_expected_transfer_len_data(cmd), 15*1024*1024); cmd->data_len = cmd->bufflen; if (cmd->data_direction == SCST_DATA_BIDI) @@ -784,21 +803,22 @@ static int scst_parse_cmd(struct scst_cmd *cmd) if (scst_cmd_is_expected_set(cmd)) { #ifdef CONFIG_SCST_USE_EXPECTED_VALUES if (unlikely((cmd->data_direction != cmd->expected_data_direction) || - (cmd->bufflen != cmd->expected_transfer_len) || + !scst_bufflen_eq_expecten_len(cmd) || (cmd->out_bufflen != cmd->expected_out_transfer_len))) { TRACE(TRACE_MINOR, "Expected values don't match " "decoded ones: data_direction %d, " "expected_data_direction %d, " - "bufflen %d, expected_transfer_len %d, " - "out_bufflen %d, expected_out_transfer_len %d", + "bufflen %d, expected len data %d, expected len " + "DIF %d, out_bufflen %d, expected_out_transfer_len %d", cmd->data_direction, cmd->expected_data_direction, - cmd->bufflen, cmd->expected_transfer_len, + cmd->bufflen, scst_cmd_get_expected_transfer_len_data(cmd), + scst_cmd_get_expected_transfer_len_dif(cmd), cmd->out_bufflen, cmd->expected_out_transfer_len); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); cmd->data_direction = cmd->expected_data_direction; - cmd->bufflen = cmd->expected_transfer_len; + cmd->bufflen = scst_cmd_get_expected_transfer_len_data(cmd); cmd->data_len = cmd->bufflen; cmd->out_bufflen = cmd->expected_out_transfer_len; cmd->resid_possible = 1; @@ -822,13 +842,15 @@ static int scst_parse_cmd(struct scst_cmd *cmd) goto out_done; } } - if (unlikely(cmd->bufflen != cmd->expected_transfer_len)) { + if (unlikely(!scst_bufflen_eq_expecten_len(cmd))) { TRACE(TRACE_MINOR, "Warning: expected " - "transfer length %d for opcode %s " + "transfer length %d (DIF %d) for opcode %s " "(handler %s, target %s) doesn't match " "decoded value %d", - cmd->expected_transfer_len, scst_get_opcode_name(cmd), - devt->name, cmd->tgtt->name, cmd->bufflen); + scst_cmd_get_expected_transfer_len_data(cmd), + scst_cmd_get_expected_transfer_len_dif(cmd), + scst_get_opcode_name(cmd), devt->name, + cmd->tgtt->name, cmd->bufflen); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); if ((cmd->expected_data_direction & SCST_DATA_READ) || @@ -877,14 +899,16 @@ set_res: TRACE(TRACE_SCSI, "op_name <%s> (cmd %p), direction=%d " "(expected %d, set %s), lba=%lld, bufflen=%d, data len %lld, " - "out_bufflen=%d, (expected len %d, out expected len %d), " - "flags=0x%x, internal %d, naca %d", cmd->op_name, cmd, - cmd->data_direction, cmd->expected_data_direction, + "out_bufflen=%d, (expected len data %d, expected len DIF %d, " + "out expected len %d), flags=0x%x, internal %d, naca %d", + cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", (unsigned long long)cmd->lba, cmd->bufflen, (long long)cmd->data_len, cmd->out_bufflen, - cmd->expected_transfer_len, cmd->expected_out_transfer_len, - cmd->op_flags, cmd->internal, cmd->cmd_naca); + scst_cmd_get_expected_transfer_len_data(cmd), + scst_cmd_get_expected_transfer_len_dif(cmd), + cmd->expected_out_transfer_len, cmd->op_flags, cmd->internal, + cmd->cmd_naca); #ifdef CONFIG_SCST_EXTRACHECKS switch (state) { @@ -1044,7 +1068,7 @@ static void scst_set_write_len(struct scst_cmd *cmd) goto out; } else { cmd->write_len = min(cmd->bufflen, - cmd->expected_transfer_len); + scst_cmd_get_expected_transfer_len_data(cmd)); if (cmd->write_len == cmd->bufflen) goto out; } @@ -1162,13 +1186,18 @@ alloc: TRACE_MEM("tgt_i_data_buf_alloced set (cmd %p)", cmd); cmd->sg = cmd->tgt_i_sg; cmd->sg_cnt = cmd->tgt_i_sg_cnt; + cmd->dif_sg = cmd->tgt_i_dif_sg; + cmd->dif_sg_cnt = cmd->tgt_i_dif_sg_cnt; cmd->out_sg = cmd->tgt_out_sg; cmd->out_sg_cnt = cmd->tgt_out_sg_cnt; r = 0; } else { TRACE_MEM("Both *_data_buf_alloced set (cmd %p, sg %p, " - "sg_cnt %d, tgt_i_sg %p, tgt_i_sg_cnt %d)", cmd, cmd->sg, - cmd->sg_cnt, cmd->tgt_i_sg, cmd->tgt_i_sg_cnt); + "sg_cnt %d, dif_sg %p, dif_sg_cnt %d, tgt_i_sg %p, " + "tgt_i_sg_cnt %d, tgt_i_dif_sg %p, tgt_i_dif_sg_cnt %d)", + cmd, cmd->sg, cmd->sg_cnt, cmd->dif_sg, cmd->dif_sg_cnt, + cmd->tgt_i_sg, cmd->tgt_i_sg_cnt, cmd->tgt_i_dif_sg, + cmd->tgt_i_dif_sg_cnt); r = 0; } @@ -2760,6 +2789,15 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only) goto out; } + if (unlikely(test_bit(SCST_TGT_DEV_FORWARDING, &cmd->tgt_dev->tgt_dev_flags))) { + /* + * All the checks are supposed to be done on the + * forwarding requester's side. + */ + res = 0; + goto out; + } + /* * There's no race here, because we need to trace commands sent * *after* dev_double_ua_possible flag was set. @@ -3409,6 +3447,7 @@ static int scst_check_sense(struct scst_cmd *cmd) if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) && scst_sense_valid(cmd->sense)) { + TRACE(TRACE_SCSI, "cmd %p with valid sense received", cmd); PRINT_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense, cmd->sense_valid_len); @@ -3535,6 +3574,7 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) TRACE_ENTRY(); +again: rc = scst_check_auto_sense(cmd); if (unlikely(rc)) { if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) @@ -3565,16 +3605,22 @@ next: goto out; } - rc = scsi_status_is_good(cmd->status); - if (likely(rc)) { - unsigned char type = cmd->dev->type; + if (likely(scst_cmd_completed_good(cmd))) { + if (cmd->deferred_dif_read_check) { + int rc = scst_dif_process_read(cmd); + if (unlikely(rc != 0)) { + cmd->deferred_dif_read_check = 0; + goto again; + } + } + if (unlikely((cmd->cdb[0] == MODE_SENSE || cmd->cdb[0] == MODE_SENSE_10)) && (cmd->tgt_dev->tgt_dev_rd_only || cmd->dev->swp) && - (type == TYPE_DISK || - type == TYPE_WORM || - type == TYPE_MOD || - type == TYPE_TAPE)) { + (cmd->dev->type == TYPE_DISK || + cmd->dev->type == TYPE_WORM || + cmd->dev->type == TYPE_MOD || + cmd->dev->type == TYPE_TAPE)) { int32_t length; uint8_t *address; bool err = false; @@ -4128,6 +4174,7 @@ static int scst_finish_cmd(struct scst_cmd *cmd) int res; struct scst_session *sess = cmd->sess; struct scst_io_stat_entry *stat; + int block_shift, align_len; TRACE_ENTRY(); @@ -4156,6 +4203,18 @@ static int scst_finish_cmd(struct scst_cmd *cmd) stat = &sess->io_stats[cmd->data_direction]; stat->cmd_count++; stat->io_byte_count += cmd->bufflen + cmd->out_bufflen; + if (likely(cmd->dev != NULL)) { + block_shift = cmd->dev->block_shift; + /* Let's track only 4K unaligned cmds at the moment */ + align_len = (block_shift != 0) ? 4095 : 0; + } else { + block_shift = 0; + align_len = 0; + } + + if (unlikely(((cmd->lba << block_shift) & align_len) != 0) || + unlikely(((cmd->bufflen + cmd->out_bufflen) & align_len) != 0)) + stat->unaligned_cmd_count++; list_del(&cmd->sess_cmd_list_entry); @@ -4430,8 +4489,8 @@ static int scst_translate_lun(struct scst_cmd *cmd) scst_put(cmd->cpu_cmd_counter); } } else { - TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping"); scst_put(cmd->cpu_cmd_counter); + TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping"); res = 1; } @@ -5013,8 +5072,8 @@ static int scst_get_mgmt(struct scst_mgmt_cmd *mcmd) if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags) && !test_bit(SCST_FLAG_SUSPENDING, &scst_flags))) { - TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping"); scst_put(mcmd->cpu_cmd_counter); + TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping"); res = 1; goto out; } @@ -5423,18 +5482,21 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, if (mstb->done_counted || mstb->finish_counted) { unsigned long t; + char state_name[32]; if (mcmd->fn != SCST_PR_ABORT_ALL) t = TRACE_MGMT; else t = TRACE_MGMT_DEBUG; TRACE(t, "cmd %p (tag %llu, " - "sn %u) being executed/xmitted (state %d, " + "sn %u) being executed/xmitted (state %s, " "op %s, proc time %ld sec., timeout %d sec.), " "deferring ABORT (cmd_done_wait_count %d, " "cmd_finish_wait_count %d, internal %d, mcmd " "fn %d (mcmd %p), initiator %s, target %s)", cmd, (unsigned long long int)cmd->tag, - cmd->sn, cmd->state, scst_get_opcode_name(cmd), + cmd->sn, scst_get_cmd_state_name(state_name, + sizeof(state_name), cmd->state), + scst_get_opcode_name(cmd), (long)(jiffies - cmd->start_time) / HZ, cmd->timeout / HZ, mcmd->cmd_done_wait_count, mcmd->cmd_finish_wait_count, cmd->internal, @@ -5506,9 +5568,13 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd) break; default: - PRINT_CRIT_ERROR("Wrong mcmd %p state %d (fn %d, " + { + char fn_name[16], state_name[32]; + PRINT_CRIT_ERROR("Wrong mcmd %p state %s (fn %s, " "cmd_finish_wait_count %d, cmd_done_wait_count %d)", - mcmd, mcmd->state, mcmd->fn, + mcmd, scst_get_mcmd_state_name(state_name, + sizeof(state_name), mcmd->state), + scst_get_tm_fn_name(fn_name, sizeof(fn_name), mcmd->fn), mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count); #if !defined(__CHECKER__) spin_unlock_irq(&scst_mcmd_lock); @@ -5516,6 +5582,7 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd) res = -1; sBUG(); } + } spin_unlock_irq(&scst_mcmd_lock); @@ -5959,7 +6026,14 @@ static int scst_target_reset(struct scst_mgmt_cmd *mcmd) /* dev->scsi_dev must be non-NULL here */ TRACE(TRACE_MGMT, "Resetting host %d bus ", dev->scsi_dev->host->host_no); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + { + int arg = SG_SCSI_RESET_TARGET; + + rc = scsi_ioctl_reset(dev->scsi_dev, + (__force __user int *)&arg); + } +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) rc = scsi_reset_provider(dev->scsi_dev, SCSI_TRY_RESET_TARGET); #else rc = scsi_reset_provider(dev->scsi_dev, SCSI_TRY_RESET_BUS); @@ -6022,7 +6096,16 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd) if (dev->scsi_dev != NULL) { TRACE(TRACE_MGMT, "Resetting host %d bus ", dev->scsi_dev->host->host_no); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + { + int arg = SG_SCSI_RESET_DEVICE; + + rc = scsi_ioctl_reset(dev->scsi_dev, + (__force __user int *)&arg); + } +#else rc = scsi_reset_provider(dev->scsi_dev, SCSI_TRY_RESET_DEVICE); +#endif TRACE(TRACE_MGMT, "scsi_reset_provider(%s) returned %d", dev->virt_name, rc); #if 0 @@ -6529,13 +6612,18 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd) goto out; default: - PRINT_CRIT_ERROR("Wrong mcmd %p state %d (fn %d, " + { + char fn_name[16], state_name[32]; + PRINT_CRIT_ERROR("Wrong mcmd %p state %s (fn %s, " "cmd_finish_wait_count %d, cmd_done_wait_count " - "%d)", mcmd, mcmd->state, mcmd->fn, + "%d)", mcmd, scst_get_mcmd_state_name(state_name, + sizeof(state_name), mcmd->state), + scst_get_tm_fn_name(fn_name, sizeof(fn_name), mcmd->fn), mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count); sBUG(); } + } } out: @@ -6722,6 +6810,7 @@ int scst_rx_mgmt_fn(struct scst_session *sess, { int res = -EFAULT; struct scst_mgmt_cmd *mcmd = NULL; + char state_name[32]; TRACE_ENTRY(); @@ -6757,11 +6846,13 @@ int scst_rx_mgmt_fn(struct scst_session *sess, mcmd->cmd_sn = params->cmd_sn; if (params->fn < SCST_UNREG_SESS_TM) - TRACE(TRACE_MGMT, "TM fn %d (mcmd %p, initiator %s, target %s)", - params->fn, mcmd, sess->initiator_name, - sess->tgt->tgt_name); + TRACE(TRACE_MGMT, "TM fn %s/%d (mcmd %p, initiator %s, target %s)", + scst_get_tm_fn_name(state_name, sizeof(state_name), params->fn), + params->fn, mcmd, sess->initiator_name, sess->tgt->tgt_name); else - TRACE_MGMT_DBG("TM fn %d (mcmd %p)", params->fn, mcmd); + TRACE_MGMT_DBG("TM fn %s/%d (mcmd %p)", + scst_get_tm_fn_name(state_name, sizeof(state_name), params->fn), + params->fn, mcmd); TRACE_MGMT_DBG("sess=%p, tag_set %d, tag %lld, lun_set %d, " "lun=%lld, cmd_sn_set %d, cmd_sn %d, priv %p", sess, diff --git a/scst/src/scst_tg.c b/scst/src/scst_tg.c index 62c7b70f5..d48af7c6d 100644 --- a/scst/src/scst_tg.c +++ b/scst/src/scst_tg.c @@ -3,7 +3,7 @@ * * SCSI target group related code. * - * Copyright (C) 2011 - 2014 Bart Van Assche . + * Copyright (C) 2011 - 2015 Bart Van Assche . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst_local/in-tree/Makefile-3.19 b/scst_local/in-tree/Makefile-3.19 new file mode 100644 index 000000000..8cbbbff63 --- /dev/null +++ b/scst_local/in-tree/Makefile-3.19 @@ -0,0 +1,2 @@ +obj-$(CONFIG_SCST_LOCAL) += scst_local.o + diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 8f3e182f8..92a971326 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 - 2010 Richard Sharpe * Copyright (C) 1992 Eric Youngdale - * Copyright (C) 2008 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2008 - 2015 Vladislav Bolkhovitin * * Simulate a host adapter and an SCST target adapter back to back * @@ -1018,6 +1018,12 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt, } scst_cmd_set_tag(scst_cmd, SCpnt->tag); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + if (SCpnt->device->tagged_supported && SCpnt->device->simple_tags) + scst_cmd_set_queue_type(scst_cmd, SCST_CMD_QUEUE_SIMPLE); + else + scst_cmd_set_queue_type(scst_cmd, SCST_CMD_QUEUE_UNTAGGED); +#else switch (scsi_get_tag_type(SCpnt->device)) { case MSG_SIMPLE_TAG: scst_cmd_set_queue_type(scst_cmd, SCST_CMD_QUEUE_SIMPLE); @@ -1033,6 +1039,7 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt, scst_cmd_set_queue_type(scst_cmd, SCST_CMD_QUEUE_UNTAGGED); break; } +#endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) /* @@ -1162,7 +1169,14 @@ static int scst_local_get_max_queue_depth(struct scsi_device *sdev) return res; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + +static int scst_local_change_queue_depth(struct scsi_device *sdev, int depth) +{ + return scsi_change_queue_depth(sdev, depth); +} + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || \ defined(CONFIG_SUSE_KERNEL) || \ !(!defined(RHEL_RELEASE_CODE) || \ RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) @@ -1242,10 +1256,12 @@ static int scst_local_slave_configure(struct scsi_device *sdev) PRINT_INFO("Configuring queue depth %d on sdev %p (tagged supported %d)", mqd, sdev, sdev->tagged_supported); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) if (sdev->tagged_supported) scsi_activate_tcq(sdev, mqd); else scsi_deactivate_tcq(sdev, mqd); +#endif TRACE_EXIT(); return 0; @@ -1370,14 +1386,6 @@ out: return res; } -static int scst_local_targ_detect(struct scst_tgt_template *tgt_template) -{ - TRACE_ENTRY(); - - TRACE_EXIT(); - return 0; -}; - static int scst_local_targ_release(struct scst_tgt *tgt) { TRACE_ENTRY(); @@ -1574,7 +1582,6 @@ static struct scst_tgt_template scst_local_targ_tmpl = { .mgmt_cmd_help = " echo \"add_session target_name session_name\" >mgmt\n" " echo \"del_session target_name session_name\" >mgmt\n", #endif - .detect = scst_local_targ_detect, .release = scst_local_targ_release, .close_session = scst_local_close_session, .pre_exec = scst_local_targ_pre_exec, @@ -1621,6 +1628,9 @@ static struct scsi_host_template scst_lcl_ini_driver_template = { #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)) .eh_target_reset_handler = scst_local_target_reset, #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + .use_blk_tags = true, +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || \ defined(CONFIG_SUSE_KERNEL) || \ !(!defined(RHEL_RELEASE_CODE) || \ diff --git a/scstadmin/Makefile b/scstadmin/Makefile index 5d7b1be0a..eebdbc9fe 100644 --- a/scstadmin/Makefile +++ b/scstadmin/Makefile @@ -94,11 +94,11 @@ install install_vendor: for s in iscsi-scst qla2x00t; do \ { $(call REMOVE_FN,$$s); } >/dev/null 2>&1; \ done - echo - echo 'If you want SCST to start automatically at boot time, run' \ + @echo + @echo 'If you want SCST to start automatically at boot time, run' \ 'the following command:' - $(call ECHO_INSTALL_FN,scst) - echo + @$(call ECHO_INSTALL_FN,scst) + @echo uninstall: if [ -e $(DESTDIR)$(DEFAULTDIR)/scst ]; then \ diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm index 0b2bb7065..a74d5ad03 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm @@ -4701,7 +4701,8 @@ sub sessions { my $linked = readlink $pPath; my $g = SCST_GROUPS; my $l = SCST_LUNS; - if ($linked =~ /\.\.\/\.\.\/$g\/(.*)\/$l/) { + if (defined($linked) && + $linked =~ /\.\.\/\.\.\/$g\/(.*)\/$l/) { my $group = $1; $_sessions{$session}->{$attribute} = $self->luns($driver, $target, $group); diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf index da8c75c37..f0c5264f2 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf @@ -27,6 +27,11 @@ TARGET_DRIVER scst_local { INITIATOR ini3 } + + GROUP group_without_luns_with_attrs { + + addr_method FLAT + } } } diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf index b4988b721..9e56bc187 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf @@ -33,6 +33,11 @@ TARGET_DRIVER scst_local { INITIATOR ini3 } + + GROUP group_without_luns_with_attrs { + + addr_method FLAT + } } } diff --git a/scstadmin/scstadmin.sysfs/scstadmin b/scstadmin/scstadmin.sysfs/scstadmin index 98f783fcf..a0f7ebb44 100755 --- a/scstadmin/scstadmin.sysfs/scstadmin +++ b/scstadmin/scstadmin.sysfs/scstadmin @@ -643,14 +643,14 @@ sub getArgs { if ((defined($addGroup) || defined($removeGroup)) && (($driver eq '') || ($target eq ''))) { - print "Please specify -driver and -target with -add_group/-remove_group.\n"; + print "Please specify -driver and -target with -add_group/-rem_group.\n"; exit 1; } if ((defined($addInitiator) || defined($removeInitiator) || defined($clearInitiators)) && (($target eq '') || ($driver eq '') || ($group eq ''))) { print "Please specify -driver -target and -group with ". - "-add_init/-remove_init/-clear_inits.\n"; + "-add_init/-rem_init/-clear_inits.\n"; exit 1; } @@ -1566,12 +1566,6 @@ sub writeConfiguration { } } - if ($lun_buff || $init_buff) { - $group_buff .= " {\n"; - $group_buff .= $lun_buff; - $group_buff .= $init_buff; - } - my ($grp_attributes, $errorString) = $SCST->groupAttributes($driver, $target, $group); my $g_attribute_buff = serializeKeyAttr("\t\t\t", @@ -1581,6 +1575,13 @@ sub writeConfiguration { $grp_attributes) if ($nonkey); + if ($lun_buff || $init_buff || + $g_attribute_buff || $g_attribute_buff_nk) { + $group_buff .= " {\n"; + $group_buff .= $lun_buff; + $group_buff .= $init_buff; + } + if ($g_attribute_buff_nk) { $g_attribute_buff .= "\n" if ($g_attribute_buff); $g_attribute_buff .= "\t\t\t# Non-key attributes\n"; diff --git a/srpt/Makefile b/srpt/Makefile index 62e680454..68fcfcdbf 100644 --- a/srpt/Makefile +++ b/srpt/Makefile @@ -52,7 +52,7 @@ OFED_KERNEL_IB_RPM:=$(shell for r in mlnx-ofa_kernel compat-rdma kernel-ib; do r # Name of the OFED kernel development RPM. OFED_KERNEL_IB_DEVEL_RPM:=$(shell for r in mlnx-ofa_kernel-devel compat-rdma-devel kernel-ib-devel; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) -OFED_FLAVOR=$(shell /usr/bin/ofed_info 2>/dev/null | head -n1 | sed -n 's/^\(MLNX_OFED\|OFED-internal\).*/MOFED/p;s/^OFED-.*/OFED/p') +OFED_FLAVOR=$(shell if [ -e /usr/bin/ofed_info ]; then /usr/bin/ofed_info 2>/dev/null | head -n1 | sed -n 's/^\(MLNX_OFED\|OFED-internal\).*/MOFED/p;s/^OFED-.*/OFED/p'; else echo in-tree; fi) ifneq ($(OFED_KERNEL_IB_RPM),) ifeq ($(OFED_KERNEL_IB_RPM),compat-rdma) @@ -77,22 +77,19 @@ endif OFED_MODULE_SYMVERS:=$(OFED_KERNEL_DIR)/$(MODULE_SYMVERS) endif -HAVE_KCFLAGS = $(shell $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/kcflags KCFLAGS=-DKCFLAGS_MACRO=1 >/dev/null 2>&1 && echo true || echo false) -HAVE_PRE_CFLAGS = $(shell $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/pre_cflags PRE_CFLAGS=-DPRE_CFLAGS_MACRO=1 >/dev/null 2>&1 && echo true || echo false) -AUTOCONF_FLAGS = $(shell $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/gid_change PRE_CFLAGS="$(OFED_CFLAGS)" >/dev/null 2>&1 && echo -DHAVE_IB_EVENT_GID_CHANGE) - +AUTOCONF_FLAGS = $(shell $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/gid_change PRE_CFLAGS="$(OFED_CFLAGS)" >/dev/null 2>&1 && echo -DHAVE_IB_EVENT_GID_CHANGE) +PRE_CFLAGS=$(OFED_CFLAGS) $(AUTOCONF_FLAGS) -DOFED_FLAVOR=$(OFED_FLAVOR) all: src/$(MODULE_SYMVERS) $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src \ - PRE_CFLAGS="$(OFED_CFLAGS) $(AUTOCONF_FLAGS)" \ - KCFLAGS="$(AUTOCONF_FLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) modules + PRE_CFLAGS="$(PRE_CFLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) modules install: all @[ -z "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && \ find /lib/modules/$(KVER) -name ib_srpt.ko -exec rm {} \; ; \ true $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src \ - PRE_CFLAGS="$(OFED_CFLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) \ + PRE_CFLAGS="$(PRE_CFLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) \ $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install @@ -102,11 +99,6 @@ uninstall: src/$(MODULE_SYMVERS): $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) \ $(OFED_MODULE_SYMVERS) - @if [ "$(HAVE_KCFLAGS)" = false -a "$(HAVE_PRE_CFLAGS)" = false -a \ - -n "$(AUTOCONF_FLAGS)" ]; then \ - echo "Error: the kernel build system has not yet been patched.";\ - false; \ - fi @if [ -n "$(OFED_KERNEL_IB_RPM)" ]; then \ if [ -z "$(OFED_KERNEL_IB_DEVEL_RPM)" ]; then \ echo "Error: the OFED package $(OFED_KERNEL_IB_RPM)-devel has" \ @@ -139,8 +131,10 @@ src/$(MODULE_SYMVERS): $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) \ fi clean: - $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src clean + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/gid_change clean + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/ clean rm -f src/$(MODULE_SYMVERS) src/Module.markers src/modules.order + rm -rf conftest/pre_cflags conftest/kcflags extraclean: clean rm -f *.orig *.rej diff --git a/srpt/README b/srpt/README index 72a5929db..85bb1a4da 100644 --- a/srpt/README +++ b/srpt/README @@ -47,20 +47,6 @@ The ib_srpt kernel module supports the following parameters: parameter. Note: setting this parameter too low will cause SRP every login to fail and will cause a message similar to the following to be logged on the target system: "ib_srpt: RDMA t ... for idx ... failed with status 12". -* one_target_per_port (boolean) and -* use_node_guid_in_target_name (boolean) - ib_srpt can operate in one of the following three modes: - 1. Access control configuration per HCA and assigning a "ib_srpt_target_" - style name to each HCA. - 2. Access control configuration per HCA and referring to a HCA via its node - GUID (e.g. 0002:c903:0005:f34a). - 3. Access control configuration per HCA port and referring to a HCA via its - port GID (e.g. fe80:0000:0000:0000:0002:c903:0005:f34b). - Mode (1) is chosen if both one_target_per_port and - use_node_guid_in_target_name are false. Mode (2) is chosen if - one_target_per_port is false and use_node_guid_in_target_name is true. Mode - (3) is chosen if one_target_per_port is true. This last mode is the - default mode. * rdma_cm_port (number) A 16-bit number that specifies the port number to be registered via the RDMA/CM. Must be specified to make communication over RoCE or iWARP diff --git a/srpt/README.ofed b/srpt/README.ofed index 788b8b941..e543200ea 100644 --- a/srpt/README.ofed +++ b/srpt/README.ofed @@ -55,16 +55,6 @@ Remove any distro-provided InfiniBand drivers: rm -rf /lib/modules/$(uname -r)/kernel/drivers/infiniband rm -rf /lib/modules/$(uname -r)/kernel/drivers/net/mlx4 -Now locate the file Makefile.lib and patch it such that it supports -the variable PRE_CFLAGS: - - if [ -e /lib/modules/$(uname -r)/build/scripts/Makefile.lib ]; then - cd /lib/modules/$(uname -r)/build - else - cd /usr/src/linux-$(uname -r | sed 's/\(-default\|-trace\|-xen\)$//;s/\.[0-9]\+\.[0-9]\+\.[0-9]\+\.PTF$//') - fi - patch -p1 < ${SCST_DIR}/srpt/patches/kernel-${KV}-pre-cflags.patch - Next, download and install the OFED package. Make sure to disable OFED's SRP target driver (srpt=n) and to enable at least the kernel-ib, kernel-ib-devel and SRP tools packages. An example: diff --git a/srpt/conftest/gid_change/Makefile b/srpt/conftest/gid_change/Makefile index e81c05753..ef14a136e 100644 --- a/srpt/conftest/gid_change/Makefile +++ b/srpt/conftest/gid_change/Makefile @@ -1 +1,3 @@ +LINUXINCLUDE := $(PRE_CFLAGS) $(LINUXINCLUDE) + obj-m += gid_change.o diff --git a/srpt/conftest/kcflags/Makefile b/srpt/conftest/kcflags/Makefile deleted file mode 100644 index 59e12dda0..000000000 --- a/srpt/conftest/kcflags/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-m += kcflags.o diff --git a/srpt/conftest/kcflags/kcflags.c b/srpt/conftest/kcflags/kcflags.c deleted file mode 100644 index fff6e202f..000000000 --- a/srpt/conftest/kcflags/kcflags.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -static int modinit(void) -{ - return KCFLAGS_MACRO; -} - -module_init(modinit); diff --git a/srpt/conftest/pre_cflags/Makefile b/srpt/conftest/pre_cflags/Makefile deleted file mode 100644 index 3c8c550f2..000000000 --- a/srpt/conftest/pre_cflags/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-m += pre_cflags.o diff --git a/srpt/conftest/pre_cflags/pre_cflags.c b/srpt/conftest/pre_cflags/pre_cflags.c deleted file mode 100644 index 1602d7115..000000000 --- a/srpt/conftest/pre_cflags/pre_cflags.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -static int modinit(void) -{ - return PRE_CFLAGS_MACRO; -} - -module_init(modinit); diff --git a/srpt/patches/kernel-2.6.18-pre-cflags.patch b/srpt/patches/kernel-2.6.18-pre-cflags.patch deleted file mode 100644 index f0801cc33..000000000 --- a/srpt/patches/kernel-2.6.18-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.18/scripts/Makefile.lib 2009-05-10 20:00:05.000000000 +0200 -+++ linux-2.6.18/scripts/Makefile.lib 2009-05-10 20:00:25.000000000 +0200 -@@ -108,7 +108,7 @@ - __cpp_flags = $(call flags,_cpp_flags) - endif - --c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ -+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(PRE_CFLAGS) $(CPPFLAGS) \ - $(__c_flags) $(modkern_cflags) \ - -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) - diff --git a/srpt/patches/kernel-2.6.23-pre-cflags.patch b/srpt/patches/kernel-2.6.23-pre-cflags.patch deleted file mode 100644 index bba6b6c50..000000000 --- a/srpt/patches/kernel-2.6.23-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.23.17/scripts/Makefile.lib.orig 2011-04-10 10:29:50.000000000 -0400 -+++ linux-2.6.23.17/scripts/Makefile.lib 2011-04-10 10:30:27.000000000 -0400 -@@ -86,7 +86,7 @@ - modname_flags = $(if $(filter 1,$(words $(modname))),\ - -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))") - --_c_flags = $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(basetarget).o) -+_c_flags = $(PRE_CFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(basetarget).o) - _a_flags = $(AFLAGS) $(EXTRA_AFLAGS) $(AFLAGS_$(basetarget).o) - _cpp_flags = $(CPPFLAGS) $(EXTRA_CPPFLAGS) $(CPPFLAGS_$(@F)) - diff --git a/srpt/patches/kernel-2.6.24-pre-cflags.patch b/srpt/patches/kernel-2.6.24-pre-cflags.patch deleted file mode 100644 index 1e5b34ac8..000000000 --- a/srpt/patches/kernel-2.6.24-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.27/scripts/Makefile.lib 2009-05-10 19:02:42.000000000 +0200 -+++ linux-2.6.27/scripts/Makefile.lib 2009-05-05 19:34:57.000000000 +0200 -@@ -119,7 +119,7 @@ - __cpp_flags = $(call flags,_cpp_flags) - endif - --c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ -+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(PRE_CFLAGS) $(KBUILD_CPPFLAGS) \ - $(__c_flags) $(modkern_cflags) \ - -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) - diff --git a/srpt/patches/kernel-2.6.25-pre-cflags.patch b/srpt/patches/kernel-2.6.25-pre-cflags.patch deleted file mode 100644 index 1e5b34ac8..000000000 --- a/srpt/patches/kernel-2.6.25-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.27/scripts/Makefile.lib 2009-05-10 19:02:42.000000000 +0200 -+++ linux-2.6.27/scripts/Makefile.lib 2009-05-05 19:34:57.000000000 +0200 -@@ -119,7 +119,7 @@ - __cpp_flags = $(call flags,_cpp_flags) - endif - --c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ -+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(PRE_CFLAGS) $(KBUILD_CPPFLAGS) \ - $(__c_flags) $(modkern_cflags) \ - -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) - diff --git a/srpt/patches/kernel-2.6.26-pre-cflags.patch b/srpt/patches/kernel-2.6.26-pre-cflags.patch deleted file mode 100644 index 1e5b34ac8..000000000 --- a/srpt/patches/kernel-2.6.26-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.27/scripts/Makefile.lib 2009-05-10 19:02:42.000000000 +0200 -+++ linux-2.6.27/scripts/Makefile.lib 2009-05-05 19:34:57.000000000 +0200 -@@ -119,7 +119,7 @@ - __cpp_flags = $(call flags,_cpp_flags) - endif - --c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ -+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(PRE_CFLAGS) $(KBUILD_CPPFLAGS) \ - $(__c_flags) $(modkern_cflags) \ - -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) - diff --git a/srpt/patches/kernel-2.6.27-pre-cflags.patch b/srpt/patches/kernel-2.6.27-pre-cflags.patch deleted file mode 100644 index 1e5b34ac8..000000000 --- a/srpt/patches/kernel-2.6.27-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.27/scripts/Makefile.lib 2009-05-10 19:02:42.000000000 +0200 -+++ linux-2.6.27/scripts/Makefile.lib 2009-05-05 19:34:57.000000000 +0200 -@@ -119,7 +119,7 @@ - __cpp_flags = $(call flags,_cpp_flags) - endif - --c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ -+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(PRE_CFLAGS) $(KBUILD_CPPFLAGS) \ - $(__c_flags) $(modkern_cflags) \ - -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) - diff --git a/srpt/patches/kernel-2.6.28-pre-cflags.patch b/srpt/patches/kernel-2.6.28-pre-cflags.patch deleted file mode 100644 index 1e5b34ac8..000000000 --- a/srpt/patches/kernel-2.6.28-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.27/scripts/Makefile.lib 2009-05-10 19:02:42.000000000 +0200 -+++ linux-2.6.27/scripts/Makefile.lib 2009-05-05 19:34:57.000000000 +0200 -@@ -119,7 +119,7 @@ - __cpp_flags = $(call flags,_cpp_flags) - endif - --c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ -+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(PRE_CFLAGS) $(KBUILD_CPPFLAGS) \ - $(__c_flags) $(modkern_cflags) \ - -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) - diff --git a/srpt/patches/kernel-2.6.29-pre-cflags.patch b/srpt/patches/kernel-2.6.29-pre-cflags.patch deleted file mode 100644 index 88cc5f82d..000000000 --- a/srpt/patches/kernel-2.6.29-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.30/Makefile 2009-10-31 13:49:29.000000000 +0100 -+++ linux-2.6.30/Makefile 2009-10-31 13:49:32.000000000 +0100 -@@ -342,7 +342,7 @@ AFLAGS_KERNEL = - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) -Iinclude \ - $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -include include/linux/autoconf.h diff --git a/srpt/patches/kernel-2.6.30-pre-cflags.patch b/srpt/patches/kernel-2.6.30-pre-cflags.patch deleted file mode 100644 index 88cc5f82d..000000000 --- a/srpt/patches/kernel-2.6.30-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.30/Makefile 2009-10-31 13:49:29.000000000 +0100 -+++ linux-2.6.30/Makefile 2009-10-31 13:49:32.000000000 +0100 -@@ -342,7 +342,7 @@ AFLAGS_KERNEL = - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) -Iinclude \ - $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -include include/linux/autoconf.h diff --git a/srpt/patches/kernel-2.6.31-pre-cflags.patch b/srpt/patches/kernel-2.6.31-pre-cflags.patch deleted file mode 100644 index 88cc5f82d..000000000 --- a/srpt/patches/kernel-2.6.31-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.30/Makefile 2009-10-31 13:49:29.000000000 +0100 -+++ linux-2.6.30/Makefile 2009-10-31 13:49:32.000000000 +0100 -@@ -342,7 +342,7 @@ AFLAGS_KERNEL = - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) -Iinclude \ - $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -include include/linux/autoconf.h diff --git a/srpt/patches/kernel-2.6.32-pre-cflags.patch b/srpt/patches/kernel-2.6.32-pre-cflags.patch deleted file mode 100644 index 88cc5f82d..000000000 --- a/srpt/patches/kernel-2.6.32-pre-cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- linux-2.6.30/Makefile 2009-10-31 13:49:29.000000000 +0100 -+++ linux-2.6.30/Makefile 2009-10-31 13:49:32.000000000 +0100 -@@ -342,7 +342,7 @@ AFLAGS_KERNEL = - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) -Iinclude \ - $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -include include/linux/autoconf.h diff --git a/srpt/patches/kernel-2.6.33-pre-cflags.patch b/srpt/patches/kernel-2.6.33-pre-cflags.patch deleted file mode 100644 index 2c782c124..000000000 --- a/srpt/patches/kernel-2.6.33-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 8392b64..f5991d9 100644 ---- a/Makefile -+++ b/Makefile -@@ -349,7 +349,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h - diff --git a/srpt/patches/kernel-2.6.34-pre-cflags.patch b/srpt/patches/kernel-2.6.34-pre-cflags.patch deleted file mode 100644 index 2c782c124..000000000 --- a/srpt/patches/kernel-2.6.34-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 8392b64..f5991d9 100644 ---- a/Makefile -+++ b/Makefile -@@ -349,7 +349,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h - diff --git a/srpt/patches/kernel-2.6.35-pre-cflags.patch b/srpt/patches/kernel-2.6.35-pre-cflags.patch deleted file mode 100644 index 2c782c124..000000000 --- a/srpt/patches/kernel-2.6.35-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 8392b64..f5991d9 100644 ---- a/Makefile -+++ b/Makefile -@@ -349,7 +349,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h - diff --git a/srpt/patches/kernel-2.6.36-pre-cflags.patch b/srpt/patches/kernel-2.6.36-pre-cflags.patch deleted file mode 100644 index 2c782c124..000000000 --- a/srpt/patches/kernel-2.6.36-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 8392b64..f5991d9 100644 ---- a/Makefile -+++ b/Makefile -@@ -349,7 +349,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h - diff --git a/srpt/patches/kernel-2.6.37-pre-cflags.patch b/srpt/patches/kernel-2.6.37-pre-cflags.patch deleted file mode 100644 index 2c782c124..000000000 --- a/srpt/patches/kernel-2.6.37-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 8392b64..f5991d9 100644 ---- a/Makefile -+++ b/Makefile -@@ -349,7 +349,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h - diff --git a/srpt/patches/kernel-2.6.38-pre-cflags.patch b/srpt/patches/kernel-2.6.38-pre-cflags.patch deleted file mode 100644 index 2c782c124..000000000 --- a/srpt/patches/kernel-2.6.38-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 8392b64..f5991d9 100644 ---- a/Makefile -+++ b/Makefile -@@ -349,7 +349,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h - diff --git a/srpt/patches/kernel-2.6.39-pre-cflags.patch b/srpt/patches/kernel-2.6.39-pre-cflags.patch deleted file mode 100644 index 2c782c124..000000000 --- a/srpt/patches/kernel-2.6.39-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 8392b64..f5991d9 100644 ---- a/Makefile -+++ b/Makefile -@@ -349,7 +349,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h - diff --git a/srpt/patches/kernel-3.0-pre-cflags.patch b/srpt/patches/kernel-3.0-pre-cflags.patch deleted file mode 100644 index f73c5e686..000000000 --- a/srpt/patches/kernel-3.0-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index d018956..3c3b936 100644 ---- a/Makefile -+++ b/Makefile -@@ -357,7 +357,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h diff --git a/srpt/patches/kernel-3.1-pre-cflags.patch b/srpt/patches/kernel-3.1-pre-cflags.patch deleted file mode 100644 index f73c5e686..000000000 --- a/srpt/patches/kernel-3.1-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index d018956..3c3b936 100644 ---- a/Makefile -+++ b/Makefile -@@ -357,7 +357,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h diff --git a/srpt/patches/kernel-3.10-pre-cflags.patch b/srpt/patches/kernel-3.10-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.10-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.11-pre-cflags.patch b/srpt/patches/kernel-3.11-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.11-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.12-pre-cflags.patch b/srpt/patches/kernel-3.12-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.12-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.13-pre-cflags.patch b/srpt/patches/kernel-3.13-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.13-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.14-pre-cflags.patch b/srpt/patches/kernel-3.14-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.14-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.15-pre-cflags.patch b/srpt/patches/kernel-3.15-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.15-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.16-pre-cflags.patch b/srpt/patches/kernel-3.16-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.16-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.17-pre-cflags.patch b/srpt/patches/kernel-3.17-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.17-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.18-pre-cflags.patch b/srpt/patches/kernel-3.18-pre-cflags.patch deleted file mode 100644 index a6adaf47b..000000000 --- a/srpt/patches/kernel-3.18-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index fd80c6e..09ca4ea 100644 ---- a/Makefile -+++ b/Makefile -@@ -390,6 +390,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.2-pre-cflags.patch b/srpt/patches/kernel-3.2-pre-cflags.patch deleted file mode 100644 index f73c5e686..000000000 --- a/srpt/patches/kernel-3.2-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index d018956..3c3b936 100644 ---- a/Makefile -+++ b/Makefile -@@ -357,7 +357,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h diff --git a/srpt/patches/kernel-3.3-pre-cflags.patch b/srpt/patches/kernel-3.3-pre-cflags.patch deleted file mode 100644 index bedb76321..000000000 --- a/srpt/patches/kernel-3.3-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 1932984..74549da 100644 ---- a/Makefile -+++ b/Makefile -@@ -357,7 +357,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include $(srctree)/include/linux/kconfig.h diff --git a/srpt/patches/kernel-3.4-pre-cflags.patch b/srpt/patches/kernel-3.4-pre-cflags.patch deleted file mode 100644 index bedb76321..000000000 --- a/srpt/patches/kernel-3.4-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 1932984..74549da 100644 ---- a/Makefile -+++ b/Makefile -@@ -357,7 +357,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include $(srctree)/include/linux/kconfig.h diff --git a/srpt/patches/kernel-3.5-pre-cflags.patch b/srpt/patches/kernel-3.5-pre-cflags.patch deleted file mode 100644 index bedb76321..000000000 --- a/srpt/patches/kernel-3.5-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 1932984..74549da 100644 ---- a/Makefile -+++ b/Makefile -@@ -357,7 +357,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include $(srctree)/include/linux/kconfig.h diff --git a/srpt/patches/kernel-3.6-pre-cflags.patch b/srpt/patches/kernel-3.6-pre-cflags.patch deleted file mode 100644 index bedb76321..000000000 --- a/srpt/patches/kernel-3.6-pre-cflags.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Makefile b/Makefile -index 1932984..74549da 100644 ---- a/Makefile -+++ b/Makefile -@@ -357,7 +357,8 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage - - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option --LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -+LINUXINCLUDE := $(PRE_CFLAGS) \ -+ -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated -Iinclude \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include $(srctree)/include/linux/kconfig.h diff --git a/srpt/patches/kernel-3.7-pre-cflags.patch b/srpt/patches/kernel-3.7-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.7-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.8-pre-cflags.patch b/srpt/patches/kernel-3.8-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.8-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/patches/kernel-3.9-pre-cflags.patch b/srpt/patches/kernel-3.9-pre-cflags.patch deleted file mode 100644 index 3964ee179..000000000 --- a/srpt/patches/kernel-3.9-pre-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/Makefile b/Makefile -index 540f7b2..078307f 100644 ---- a/Makefile -+++ b/Makefile -@@ -361,6 +361,7 @@ USERINCLUDE := \ - # Use LINUXINCLUDE when you must reference the include/ directory. - # Needed to be compatible with the O= option - LINUXINCLUDE := \ -+ $(PRE_CFLAGS) \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -Iarch/$(hdr-arch)/include/generated \ - $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/src/Makefile b/srpt/src/Makefile index 5850d0554..5beb4781c 100644 --- a/srpt/src/Makefile +++ b/srpt/src/Makefile @@ -1,3 +1,4 @@ +LINUXINCLUDE := $(PRE_CFLAGS) $(LINUXINCLUDE) EXTRA_CFLAGS += -I$(SCST_INC_DIR) -include $(SUBDIRS)/../build_mode diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 9cbfeb835..30141669c 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2006 - 2009 Mellanox Technology Inc. All rights reserved. - * Copyright (C) 2008 - 2014 Bart Van Assche . + * Copyright (C) 2008 - 2015 Bart Van Assche . * Copyright (C) 2008 Vladislav Bolkhovitin * * This software is available to you under a choice of one of two @@ -33,6 +33,9 @@ * */ +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -41,14 +44,21 @@ #include #include #include -#include +#if !defined(INSIDE_KERNEL_TREE) +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#include +#else #include +#endif #if defined(CONFIG_SCST_PROC) #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) #include #include #endif #endif +#include #include "ib_srpt.h" #include "srp-ext.h" #define LOG_PREFIX "ib_srpt" /* Prefix for SCST tracing macros. */ @@ -65,12 +75,12 @@ printk(KERN_WARNING format); \ WARN_ON(true); \ } \ -} while(0); +} while (0) #endif /* Name of this kernel module. */ #define DRV_NAME "ib_srpt" -#define DRV_VERSION "3.1.0-pre" +#define DRV_VERSION "3.1.0-pre#" __stringify(OFED_FLAVOR) #define DRV_RELDATE "(not yet released)" #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) /* Flags to be used in SCST debug tracing statements. */ @@ -196,10 +206,6 @@ static const enum scst_exec_context srpt_send_context = SCST_CONTEXT_DIRECT; static struct ib_client srpt_client; static struct scst_tgt_template srpt_template; -static void srpt_unregister_mad_agent(struct srpt_device *sdev); -#ifdef CONFIG_SCST_PROC -static void srpt_unregister_procfs_entry(struct scst_tgt_template *tgt); -#endif /* CONFIG_SCST_PROC */ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch, struct srpt_send_ioctx *ioctx); static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch); @@ -284,14 +290,12 @@ static void srpt_event_handler(struct ib_event_handler *handler, struct srpt_port *sport; u8 port_num; - TRACE_ENTRY(); - sdev = ib_get_client_data(event->device, &srpt_client); if (!sdev || sdev->device != event->device) return; - TRACE_DBG("ASYNC event= %d on device= %s", - event->event, sdev->device->name); + pr_debug("ASYNC event= %d on device= %s\n", event->event, + sdev->device->name); switch (event->event) { case IB_EVENT_PORT_ERR: @@ -325,11 +329,9 @@ static void srpt_event_handler(struct ib_event_handler *handler, } break; default: - PRINT_ERROR("received unrecognized IB event %d", event->event); + pr_err("received unrecognized IB event %d\n", event->event); break; } - - TRACE_EXIT(); } /** @@ -337,7 +339,7 @@ static void srpt_event_handler(struct ib_event_handler *handler, */ static void srpt_srq_event(struct ib_event *event, void *ctx) { - TRACE_DBG("SRQ event %d", event->event); + pr_debug("SRQ event %d\n", event->event); } static const char *get_ch_state_name(enum rdma_ch_state s) @@ -362,9 +364,9 @@ static const char *get_ch_state_name(enum rdma_ch_state s) */ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) { - TRACE_DBG("QP event %d on ch=%p sess_name=%s-%d state=%s", - event->event, ch, ch->sess_name, ch->qp->qp_num, - get_ch_state_name(ch->state)); + pr_debug("QP event %d on ch=%p sess_name=%s-%d state=%s\n", + event->event, ch, ch->sess_name, ch->qp->qp_num, + get_ch_state_name(ch->state)); switch (event->event) { case IB_EVENT_COMM_EST: @@ -375,18 +377,16 @@ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) ib_cm_notify(ch->ib_cm.cm_id, event->event); #else /* Vanilla 2.6.19 kernel (or before) without OFED. */ - PRINT_ERROR("how to perform ib_cm_notify() on a" - " vanilla 2.6.18 kernel ???"); + pr_err("how to perform ib_cm_notify() on a vanilla 2.6.18 kernel ???\n"); #endif break; case IB_EVENT_QP_LAST_WQE_REACHED: - TRACE_DBG("%s-%d, state %s: received Last WQE event.", - ch->sess_name, ch->qp->qp_num, - get_ch_state_name(ch->state)); + pr_debug("%s-%d, state %s: received Last WQE event.\n", + ch->sess_name, ch->qp->qp_num, + get_ch_state_name(ch->state)); break; default: - PRINT_ERROR("received unrecognized IB QP event %d", - event->event); + pr_err("received unrecognized IB QP event %d\n", event->event); break; } } @@ -465,9 +465,10 @@ static void srpt_get_iou(struct ib_dm_mad *mad) * Architecture Specification. See also section B.7, table B.7 in the SRP * r16a document. */ -static void srpt_get_ioc(struct srpt_device *sdev, u32 slot, +static void srpt_get_ioc(struct srpt_port *sport, u32 slot, struct ib_dm_mad *mad) { + struct srpt_device *sdev = sport->sdev; struct ib_dm_ioc_profile *iocp; int send_queue_depth; @@ -571,7 +572,7 @@ static void srpt_mgmt_method_get(struct srpt_port *sp, struct ib_mad *rq_mad, break; case DM_ATTR_IOC_PROFILE: slot = be32_to_cpu(rq_mad->mad_hdr.attr_mod); - srpt_get_ioc(sp->sdev, slot, rsp_mad); + srpt_get_ioc(sp, slot, rsp_mad); break; case DM_ATTR_SVC_ENTRIES: slot = be32_to_cpu(rq_mad->mad_hdr.attr_mod); @@ -678,8 +679,6 @@ static int srpt_refresh_port(struct srpt_port *sport) int ret; char tgt_name[40]; - TRACE_ENTRY(); - memset(&port_modify, 0, sizeof(port_modify)); port_modify.set_port_cap_mask = IB_PORT_DEVICE_MGMT_SUP; port_modify.clr_port_cap_mask = 0; @@ -724,7 +723,7 @@ static int srpt_refresh_port(struct srpt_port *sport) } } - if (one_target_per_port && !sport->srpt_tgt.scst_tgt) { + if (!sport->scst_tgt) { snprintf(tgt_name, sizeof(tgt_name), "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", be16_to_cpu(((__be16 *) sport->gid.raw)[0]), @@ -735,17 +734,14 @@ static int srpt_refresh_port(struct srpt_port *sport) be16_to_cpu(((__be16 *) sport->gid.raw)[5]), be16_to_cpu(((__be16 *) sport->gid.raw)[6]), be16_to_cpu(((__be16 *) sport->gid.raw)[7])); - sport->srpt_tgt.scst_tgt = scst_register_target(&srpt_template, + sport->scst_tgt = scst_register_target(&srpt_template, tgt_name); - if (sport->srpt_tgt.scst_tgt) - scst_tgt_set_tgt_priv(sport->srpt_tgt.scst_tgt, sport); + if (sport->scst_tgt) + scst_tgt_set_tgt_priv(sport->scst_tgt, sport); else - PRINT_ERROR("Registration of target %s failed.", - tgt_name); + pr_err("Registration of target %s failed.\n", tgt_name); } - TRACE_EXIT_RES(0); - return 0; err_query_port: @@ -754,8 +750,6 @@ err_query_port: ib_modify_port(sport->sdev->device, sport->port, 0, &port_modify); err_mod_port: - TRACE_EXIT_RES(ret); - return ret; } @@ -776,7 +770,7 @@ static void srpt_unregister_mad_agent(struct srpt_device *sdev) sport = &sdev->port[i - 1]; WARN_ON(sport->port != i); if (ib_modify_port(sdev->device, i, 0, &port_modify) < 0) - PRINT_ERROR("disabling MAD processing failed."); + pr_err("disabling MAD processing failed.\n"); if (sport->mad_agent) { ib_unregister_mad_agent(sport->mad_agent); sport->mad_agent = NULL; @@ -851,8 +845,6 @@ static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev, struct srpt_ioctx **ring; int i; - TRACE_ENTRY(); - WARN_ON(ioctx_size != sizeof(struct srpt_recv_ioctx) && ioctx_size != sizeof(struct srpt_send_ioctx)); @@ -874,7 +866,6 @@ err: kfree(ring); ring = NULL; out: - TRACE_EXIT_HRES(ring); return ring; } @@ -987,8 +978,8 @@ static int srpt_post_send(struct srpt_rdma_ch *ch, ret = -ENOMEM; if (srpt_adjust_sq_wr_avail(ch, -1) < 0) { - PRINT_WARNING("ch %s-%d send queue full (needed 1)", - ch->sess_name, ch->qp->qp_num); + pr_warn("ch %s-%d send queue full (needed 1)\n", ch->sess_name, + ch->qp->qp_num); goto out; } @@ -1112,9 +1103,8 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, req_size = header_size + *data_len; data = (void *)srp_cmd + header_size; if (req_size > srp_max_req_size) { - PRINT_ERROR("Immediate data (length %d + %lld) exceeds" - " request size %d", header_size, *data_len, - srp_max_req_size); + pr_err("Immediate data (length %d + %lld) exceeds request size %d\n", + header_size, *data_len, srp_max_req_size); ret = -EINVAL; goto out; } @@ -1129,7 +1119,7 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, ioctx->recv_ioctx = recv_ioctx; if (((uintptr_t)data & 511) == 0) { sg_init_one(&ioctx->imm_sg, ioctx->imm_data, *data_len); - scst_cmd_set_tgt_sg(&ioctx->scmnd, &ioctx->imm_sg, 1); + scst_cmd_set_tgt_sg(&ioctx->cmd, &ioctx->imm_sg, 1); } } else if (fmt == SRP_DATA_DESC_DIRECT) { ioctx->n_rbuf = 1; @@ -1147,12 +1137,11 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, if (ioctx->n_rbuf > (srp_cmd->data_out_desc_cnt + srp_cmd->data_in_desc_cnt)) { - PRINT_ERROR("received unsupported SRP_CMD request type" - " (%u out + %u in != %u / %zu)", - srp_cmd->data_out_desc_cnt, - srp_cmd->data_in_desc_cnt, - be32_to_cpu(idb->table_desc.len), - sizeof(*db)); + pr_err("received unsupported SRP_CMD request type (%u out + %u in != %u / %zu)\n", + srp_cmd->data_out_desc_cnt, + srp_cmd->data_in_desc_cnt, + be32_to_cpu(idb->table_desc.len), + sizeof(*db)); ioctx->n_rbuf = 0; ret = -EINVAL; goto out; @@ -1174,7 +1163,7 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, memcpy(ioctx->rbufs, db, ioctx->n_rbuf * sizeof(*db)); *data_len = be32_to_cpu(idb->len); } else if (fmt != 0) { - PRINT_ERROR("Unsupported data format %d\n", fmt); + pr_err("Unsupported data format %d\n\n", fmt); ret = -EINVAL; } @@ -1205,8 +1194,8 @@ static int srpt_init_ch_qp(struct srpt_rdma_ch *ch, struct ib_qp *qp) ret = ib_find_cached_pkey(ch->sport->sdev->device, ch->sport->port, ch->pkey, &attr->pkey_index); if (ret < 0) - PRINT_ERROR("Translating pkey %#x failed (%d) - using index 0", - ch->pkey, ret); + pr_err("Translating pkey %#x failed (%d) - using index 0\n", + ch->pkey, ret); ret = ib_modify_qp(qp, attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS | IB_QP_PORT | @@ -1335,7 +1324,7 @@ static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch) ioctx->n_rdma_ius = 0; ioctx->rdma_ius = NULL; ioctx->mapped_sg_count = 0; - memset(&ioctx->scmnd, 0, sizeof(ioctx->scmnd)); + memset(&ioctx->cmd, 0, sizeof(ioctx->cmd)); return ioctx; } @@ -1357,8 +1346,7 @@ static void srpt_put_send_ioctx(struct srpt_send_ioctx *ioctx) /* * If the WARN_ON() below gets triggered this means that - * srpt_unmap_sg_to_ib_sge() has not been called before - * scst_tgt_cmd_done(). + * srpt_unmap_sg_to_ib_sge() has not been called. */ WARN_ON(ioctx->mapped_sg_count); @@ -1384,11 +1372,9 @@ static void srpt_put_send_ioctx(struct srpt_send_ioctx *ioctx) static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, enum scst_exec_context context) { - struct scst_cmd *scmnd = &ioctx->scmnd; + struct scst_cmd *cmd = &ioctx->cmd; enum srpt_command_state state = ioctx->state; - TRACE_ENTRY(); - switch (state) { case SRPT_STATE_NEED_DATA: ioctx->state = SRPT_STATE_DATA_IN; @@ -1403,10 +1389,10 @@ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, break; } - WARN_ON(ioctx != scst_cmd_get_tgt_priv(scmnd)); + WARN_ON(ioctx != scst_cmd_get_tgt_priv(cmd)); - TRACE_DBG("Aborting cmd with state %d and tag %lld", - state, scst_cmd_get_tag(scmnd)); + pr_debug("Aborting cmd with state %d and tag %lld\n", state, + scst_cmd_get_tag(cmd)); switch (state) { case SRPT_STATE_NEW: @@ -1416,9 +1402,9 @@ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, break; case SRPT_STATE_NEED_DATA: /* SCST_DATA_WRITE - RDMA read error or RDMA read timeout. */ - scst_set_cmd_error(scmnd, + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_write_error)); - scst_rx_data(scmnd, SCST_RX_STATUS_ERROR_SENSE_SET, context); + scst_rx_data(cmd, SCST_RX_STATUS_ERROR_SENSE_SET, context); break; case SRPT_STATE_CMD_RSP_SENT: /* @@ -1426,51 +1412,19 @@ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, * not been received in time. */ srpt_unmap_sg_to_ib_sge(ioctx->ch, ioctx); - scst_set_delivery_status(scmnd, SCST_CMD_DELIVERY_ABORTED); - scst_tgt_cmd_done(scmnd, context); + scst_set_delivery_status(cmd, SCST_CMD_DELIVERY_ABORTED); + scst_tgt_cmd_done(cmd, context); break; case SRPT_STATE_MGMT_RSP_SENT: /* * Management command response sending failed. This state is - * never reached since there is no scmnd associated with + * never reached since there is no cmd associated with * management commands. Note: the SCST core frees these * commands immediately after srpt_tsk_mgmt_done() returned. */ WARN(true, "Unexpected command state %d\n", state); break; } - - TRACE_EXIT(); -} - -static void srpt_on_abort_cmd(struct scst_cmd *cmd) -{ - struct srpt_send_ioctx *ioctx = scst_cmd_get_tgt_priv(cmd); - struct srpt_rdma_ch *ch = ioctx->ch; - - if (ch->state >= CH_DISCONNECTED) { - switch (ioctx->state) { - case SRPT_STATE_NEW: - case SRPT_STATE_DATA_IN: - case SRPT_STATE_MGMT: - case SRPT_STATE_DONE: - /* - * An SCST command thread is busy processing the - * command associated with the I/O context, so wait - * until that processing has finished. - */ - break; - case SRPT_STATE_NEED_DATA: - case SRPT_STATE_CMD_RSP_SENT: - case SRPT_STATE_MGMT_RSP_SENT: - PRINT_ERROR("Cmd %p: IB completion for idx %u has not" - " been received in time (SRPT command state" - " %d)", cmd, ioctx->ioctx.index, - ioctx->state); - srpt_abort_cmd(ioctx, SCST_CONTEXT_THREAD); - break; - } - } } /** @@ -1498,8 +1452,8 @@ static void srpt_handle_send_err_comp(struct srpt_rdma_ch *ch, u64 wr_id, srpt_put_send_ioctx(ioctx); break; case SRPT_STATE_DONE: - PRINT_ERROR("Received more than one IB error completion" - " for wr_id = %u.", (unsigned)index); + pr_err("Received more than one IB error completion for wr_id = %u.\n", + index); break; default: EXTRACHECKS_WARN_ON(true); @@ -1519,14 +1473,14 @@ static void srpt_handle_send_comp(struct srpt_rdma_ch *ch, switch (srpt_set_cmd_state(ioctx, SRPT_STATE_DONE)) { case SRPT_STATE_CMD_RSP_SENT: srpt_unmap_sg_to_ib_sge(ch, ioctx); - scst_tgt_cmd_done(&ioctx->scmnd, context); + scst_tgt_cmd_done(&ioctx->cmd, context); break; case SRPT_STATE_MGMT_RSP_SENT: srpt_put_send_ioctx(ioctx); break; case SRPT_STATE_DONE: - PRINT_ERROR("IB completion has been received too late for" - " wr_id = %u.", ioctx->ioctx.index); + pr_err("IB completion has been received too late for wr_id = %u.\n", + ioctx->ioctx.index); break; default: EXTRACHECKS_WARN_ON(true); @@ -1541,22 +1495,22 @@ static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch, enum srpt_opcode opcode, enum scst_exec_context context) { - struct scst_cmd *scmnd = &ioctx->scmnd; + struct scst_cmd *cmd = &ioctx->cmd; EXTRACHECKS_WARN_ON(ioctx->n_rdma <= 0); srpt_adjust_sq_wr_avail(ch, ioctx->n_rdma); - if (opcode == SRPT_RDMA_READ_LAST && scmnd) { + if (opcode == SRPT_RDMA_READ_LAST && cmd) { if (srpt_test_and_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA, SRPT_STATE_DATA_IN)) - scst_rx_data(scmnd, SCST_RX_STATUS_SUCCESS, context); + scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS, context); else - PRINT_ERROR("%s[%d]: wrong state = %d", __func__, - __LINE__, ioctx->state); + pr_err("%s: wrong ioctx state %d\n", __func__, + ioctx->state); } else if (opcode == SRPT_RDMA_ABORT) { ioctx->rdma_aborted = true; } else { - WARN(true, "scmnd == NULL (opcode %d)\n", opcode); + WARN(true, "cmd == NULL (opcode %d)\n", opcode); } } @@ -1568,23 +1522,21 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch, enum srpt_opcode opcode, enum scst_exec_context context) { - struct scst_cmd *scmnd = &ioctx->scmnd; + struct scst_cmd *cmd = &ioctx->cmd; enum srpt_command_state state = ioctx->state; switch (opcode) { case SRPT_RDMA_READ_LAST: if (ioctx->n_rdma <= 0) { - PRINT_ERROR("Received invalid RDMA read error" - " completion with idx %d", - ioctx->ioctx.index); + pr_err("Received invalid RDMA read error completion with idx %d\n", + ioctx->ioctx.index); break; } srpt_adjust_sq_wr_avail(ch, ioctx->n_rdma); if (state == SRPT_STATE_NEED_DATA) srpt_abort_cmd(ioctx, context); else - PRINT_ERROR("%s[%d]: wrong state = %d", __func__, - __LINE__, state); + pr_err("%s: wrong ioctx state %d\n", __func__, state); break; case SRPT_RDMA_WRITE_LAST: /* @@ -1593,10 +1545,10 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch, * processing of the associated command until the send error * completion has been received. */ - scst_set_delivery_status(scmnd, SCST_CMD_DELIVERY_ABORTED); + scst_set_delivery_status(cmd, SCST_CMD_DELIVERY_ABORTED); break; default: - PRINT_ERROR("%s[%d]: opcode = %u", __func__, __LINE__, opcode); + pr_err("%s: opcode %u\n", __func__, opcode); break; } } @@ -1623,7 +1575,7 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch, int status, const u8 *sense_data, int sense_data_len) { - struct scst_cmd *cmd = &ioctx->scmnd; + struct scst_cmd *cmd = &ioctx->cmd; struct srp_rsp *srp_rsp; int resid, max_sense_len; @@ -1659,14 +1611,14 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch, } } - if (!scst_sense_valid(sense_data)) + if (!scst_sense_valid(sense_data)) { sense_data_len = 0; - else { + } else { BUILD_BUG_ON(MIN_MAX_RSP_SIZE <= sizeof(*srp_rsp)); max_sense_len = ch->max_ti_iu_len - sizeof(*srp_rsp); if (sense_data_len > max_sense_len) { - PRINT_WARNING("truncated sense data from %d to %d" - " bytes", sense_data_len, max_sense_len); + pr_warn("truncated sense data from %d to %d bytes\n", + sense_data_len, max_sense_len); sense_data_len = max_sense_len; } @@ -1725,7 +1677,7 @@ static int srpt_handle_cmd(struct srpt_rdma_ch *ch, struct srpt_send_ioctx *send_ioctx, enum scst_exec_context context) { - struct scst_cmd *scmnd; + struct scst_cmd *cmd; struct srp_cmd *srp_cmd; scst_data_direction dir; u64 data_len; @@ -1735,47 +1687,47 @@ static int srpt_handle_cmd(struct srpt_rdma_ch *ch, srp_cmd = recv_ioctx->ioctx.buf + recv_ioctx->ioctx.offset; - scmnd = &send_ioctx->scmnd; - ret = scst_rx_cmd_prealloced(scmnd, ch->scst_sess, (u8 *) &srp_cmd->lun, + cmd = &send_ioctx->cmd; + ret = scst_rx_cmd_prealloced(cmd, ch->sess, (u8 *) &srp_cmd->lun, sizeof(srp_cmd->lun), srp_cmd->cdb, sizeof(srp_cmd->cdb), in_interrupt()); if (ret) { - PRINT_ERROR("tag 0x%llx: SCST command initialization failed", - srp_cmd->tag); + pr_err("tag 0x%llx: SCST command initialization failed\n", + srp_cmd->tag); goto err; } ret = srpt_get_desc_tbl(recv_ioctx, send_ioctx, srp_cmd, &dir, &data_len); if (ret) { - PRINT_ERROR("0x%llx: parsing SRP descriptor table failed.", - srp_cmd->tag); - scst_set_cmd_error(scmnd, + pr_err("0x%llx: parsing SRP descriptor table failed.\n", + srp_cmd->tag); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); } switch (srp_cmd->task_attr) { case SRP_CMD_HEAD_OF_Q: - scst_cmd_set_queue_type(scmnd, SCST_CMD_QUEUE_HEAD_OF_QUEUE); + scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE); break; case SRP_CMD_ORDERED_Q: - scst_cmd_set_queue_type(scmnd, SCST_CMD_QUEUE_ORDERED); + scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_ORDERED); break; case SRP_CMD_SIMPLE_Q: - scst_cmd_set_queue_type(scmnd, SCST_CMD_QUEUE_SIMPLE); + scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_SIMPLE); break; case SRP_CMD_ACA: - scst_cmd_set_queue_type(scmnd, SCST_CMD_QUEUE_ACA); + scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_ACA); break; default: - scst_cmd_set_queue_type(scmnd, SCST_CMD_QUEUE_ORDERED); + scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_ORDERED); break; } - scst_cmd_set_tag(scmnd, srp_cmd->tag); - scst_cmd_set_tgt_priv(scmnd, send_ioctx); - scst_cmd_set_expected(scmnd, dir, data_len); - scst_cmd_init_done(scmnd, context); + scst_cmd_set_tag(cmd, srp_cmd->tag); + scst_cmd_set_tgt_priv(cmd, send_ioctx); + scst_cmd_set_expected(cmd, dir, data_len); + scst_cmd_init_done(cmd, context); return 0; @@ -1814,51 +1766,50 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch, srp_tsk = recv_ioctx->ioctx.buf + recv_ioctx->ioctx.offset; - TRACE_DBG("recv_tsk_mgmt= %d for task_tag= %lld" - " using tag= %lld ch= %p sess= %p", - srp_tsk->tsk_mgmt_func, srp_tsk->task_tag, srp_tsk->tag, - ch, ch->scst_sess); + pr_debug("recv_tsk_mgmt= %d for task_tag= %lld using tag= %lld ch= %p sess= %p\n", + srp_tsk->tsk_mgmt_func, srp_tsk->task_tag, srp_tsk->tag, + ch, ch->sess); send_ioctx->tsk_mgmt.tag = srp_tsk->tag; switch (srp_tsk->tsk_mgmt_func) { case SRP_TSK_ABORT_TASK: - TRACE_DBG("Processing SRP_TSK_ABORT_TASK"); - ret = scst_rx_mgmt_fn_tag(ch->scst_sess, SCST_ABORT_TASK, + pr_debug("Processing SRP_TSK_ABORT_TASK\n"); + ret = scst_rx_mgmt_fn_tag(ch->sess, SCST_ABORT_TASK, srp_tsk->task_tag, in_interrupt(), send_ioctx); break; case SRP_TSK_ABORT_TASK_SET: - TRACE_DBG("Processing SRP_TSK_ABORT_TASK_SET"); - ret = scst_rx_mgmt_fn_lun(ch->scst_sess, SCST_ABORT_TASK_SET, + pr_debug("Processing SRP_TSK_ABORT_TASK_SET\n"); + ret = scst_rx_mgmt_fn_lun(ch->sess, SCST_ABORT_TASK_SET, &srp_tsk->lun, sizeof(srp_tsk->lun), in_interrupt(), send_ioctx); break; case SRP_TSK_CLEAR_TASK_SET: - TRACE_DBG("Processing SRP_TSK_CLEAR_TASK_SET"); - ret = scst_rx_mgmt_fn_lun(ch->scst_sess, SCST_CLEAR_TASK_SET, + pr_debug("Processing SRP_TSK_CLEAR_TASK_SET\n"); + ret = scst_rx_mgmt_fn_lun(ch->sess, SCST_CLEAR_TASK_SET, &srp_tsk->lun, sizeof(srp_tsk->lun), in_interrupt(), send_ioctx); break; case SRP_TSK_LUN_RESET: - TRACE_DBG("Processing SRP_TSK_LUN_RESET"); - ret = scst_rx_mgmt_fn_lun(ch->scst_sess, SCST_LUN_RESET, + pr_debug("Processing SRP_TSK_LUN_RESET\n"); + ret = scst_rx_mgmt_fn_lun(ch->sess, SCST_LUN_RESET, &srp_tsk->lun, sizeof(srp_tsk->lun), in_interrupt(), send_ioctx); break; case SRP_TSK_CLEAR_ACA: - TRACE_DBG("Processing SRP_TSK_CLEAR_ACA"); - ret = scst_rx_mgmt_fn_lun(ch->scst_sess, SCST_CLEAR_ACA, + pr_debug("Processing SRP_TSK_CLEAR_ACA\n"); + ret = scst_rx_mgmt_fn_lun(ch->sess, SCST_CLEAR_ACA, &srp_tsk->lun, sizeof(srp_tsk->lun), in_interrupt(), send_ioctx); break; default: - TRACE_DBG("Unsupported task management function."); + pr_debug("Unsupported task management function.\n"); } if (ret != 0) { - PRINT_ERROR("Processing task management function %d failed: %d", - srp_tsk->tsk_mgmt_func, ret); + pr_err("Processing task management function %d failed: %d\n", + srp_tsk->tsk_mgmt_func, ret); srpt_put_send_ioctx(send_ioctx); } } @@ -1925,19 +1876,19 @@ srpt_handle_new_iu(struct srpt_rdma_ch *ch, srpt_handle_tsk_mgmt(ch, recv_ioctx, send_ioctx); break; case SRP_I_LOGOUT: - PRINT_ERROR("Not yet implemented: SRP_I_LOGOUT"); + pr_err("Not yet implemented: SRP_I_LOGOUT\n"); break; case SRP_CRED_RSP: - TRACE_DBG("received SRP_CRED_RSP"); + pr_debug("received SRP_CRED_RSP\n"); break; case SRP_AER_RSP: - TRACE_DBG("received SRP_AER_RSP"); + pr_debug("received SRP_AER_RSP\n"); break; case SRP_RSP: - PRINT_ERROR("Received SRP_RSP"); + pr_err("Received SRP_RSP\n"); break; default: - PRINT_ERROR("received IU with unknown opcode 0x%x", opcode); + pr_err("received IU with unknown opcode 0x%x\n", opcode); break; } @@ -1967,7 +1918,7 @@ static void srpt_process_rcv_completion(struct ib_cq *cq, req_lim = srpt_adjust_req_lim(ch, -1, 0); if (unlikely(req_lim < 0)) - PRINT_ERROR("req_lim = %d < 0", req_lim); + pr_err("req_lim = %d < 0\n", req_lim); if (ch->sport->sdev->use_srq) ioctx = ch->sport->sdev->ioctx_ring[index]; else @@ -1975,8 +1926,8 @@ static void srpt_process_rcv_completion(struct ib_cq *cq, ioctx->byte_len = wc->byte_len; srpt_handle_new_iu(ch, ioctx, srpt_new_iu_context); } else { - PRINT_INFO("receiving failed for idx %u with status %d", - index, wc->status); + pr_info("receiving failed for idx %u with status %d\n", index, + wc->status); } } @@ -2036,19 +1987,19 @@ static void srpt_process_send_completion(struct ib_cq *cq, } } else { if (opcode == SRPT_SEND) { - PRINT_INFO("sending response for idx %u failed with" - " status %d", index, wc->status); + pr_info("sending response for idx %u failed with status %d\n", + index, wc->status); srpt_handle_send_err_comp(ch, wc->wr_id, srpt_send_context); } else if (opcode == SRPT_RDMA_READ_LAST || opcode == SRPT_RDMA_WRITE_LAST) { - PRINT_INFO("RDMA t %d for idx %u failed with status %d.%s", - opcode, index, wc->status, - wc->status == IB_WC_RETRY_EXC_ERR ? - " If this has not been triggered by a cable pull, please consider to increase the subnet timeout parameter on the IB switch." : - wc->status == IB_WC_WR_FLUSH_ERR ? - " If this has not been triggered by a cable pull, please check the involved IB HCA's and cables." : - ""); + pr_info("RDMA t %d for idx %u failed with status %d.%s\n", + opcode, index, wc->status, + wc->status == IB_WC_RETRY_EXC_ERR ? + " If this has not been triggered by a cable pull, please consider to increase the subnet timeout parameter on the IB switch." : + wc->status == IB_WC_WR_FLUSH_ERR ? + " If this has not been triggered by a cable pull, please check the involved IB HCA's and cables." : + ""); srpt_handle_rdma_err_comp(ch, ch->ioctx_ring[index], opcode, srpt_xmt_rsp_context); } else if (opcode == SRPT_RDMA_ZEROLENGTH_WRITE) { @@ -2124,11 +2075,10 @@ static void srpt_free_ch(struct kref *kref) kfree(ch); } -static void srpt_unreg_sess(struct scst_session *scst_sess) +static void srpt_unreg_ch(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(scst_sess); - struct srpt_device *sdev = ch->sport->sdev; - struct srpt_tgt *srpt_tgt = ch->srpt_tgt; + struct srpt_port *sport = ch->sport; + struct srpt_device *sdev = sport->sdev; kthread_stop(ch->thread); @@ -2147,17 +2097,22 @@ static void srpt_unreg_sess(struct scst_session *scst_sess) ib_destroy_cm_id(ch->ib_cm.cm_id); /* - * Invoke wake_up() inside the lock to avoid that srpt_tgt disappears + * Invoke wake_up() inside the lock to avoid that sport disappears * after list_del() and before wake_up() has been invoked. */ - mutex_lock(&srpt_tgt->mutex); + mutex_lock(&sport->mutex); list_del(&ch->list); - wake_up(&srpt_tgt->ch_releaseQ); - mutex_unlock(&srpt_tgt->mutex); + wake_up(&sport->ch_releaseQ); + mutex_unlock(&sport->mutex); kref_put(&ch->kref, srpt_free_ch); } +static void srpt_unreg_sess(struct scst_session *sess) +{ + srpt_unreg_ch(scst_sess_get_tgt_priv(sess)); +} + static int srpt_compl_thread(void *arg) { enum { poll_budget = 65536 }; @@ -2188,9 +2143,9 @@ static int srpt_compl_thread(void *arg) } set_current_state(TASK_RUNNING); - TRACE_DBG("%s-%d: about to invoke scst_unregister_session()", - ch->sess_name, ch->qp->qp_num); - scst_unregister_session(ch->scst_sess, false, srpt_unreg_sess); + pr_debug("%s-%d: about to unregister this session()\n", + ch->sess_name, ch->qp->qp_num); + scst_unregister_session(ch->sess, false, srpt_unreg_sess); while (!kthread_should_stop()) schedule_timeout(DIV_ROUND_UP(HZ, 10)); @@ -2224,8 +2179,8 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) #endif if (IS_ERR(ch->cq)) { ret = PTR_ERR(ch->cq); - PRINT_ERROR("failed to create CQ: cqe %d; c.v. %d; ret %d", - ch->rq_size + srpt_sq_size, ch->comp_vector, ret); + pr_err("failed to create CQ: cqe %d; c.v. %d; ret %d\n", + ch->rq_size + srpt_sq_size, ch->comp_vector, ret); goto out; } @@ -2258,25 +2213,25 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) ret = rdma_create_qp(ch->rdma_cm.cm_id, sdev->pd, qp_init); ch->qp = ch->rdma_cm.cm_id->qp; if (ret) - PRINT_ERROR("failed to create queue pair (%d)", ret); + pr_err("failed to create queue pair (%d)\n", ret); } else { ch->qp = ib_create_qp(sdev->pd, qp_init); if (!IS_ERR(ch->qp)) { ret = srpt_init_ch_qp(ch, ch->qp); if (ret) { - PRINT_ERROR("srpt_init_ch_qp(%#x) failed (%d)", - ch->qp->qp_num, ret); + pr_err("srpt_init_ch_qp(%#x) failed (%d)\n", + ch->qp->qp_num, ret); ib_destroy_qp(ch->qp); } } else { ret = PTR_ERR(ch->qp); - PRINT_ERROR("failed to create queue pair (%d)", ret); + pr_err("failed to create queue pair (%d)\n", ret); } } if (ret) goto err_destroy_cq; - TRACE_DBG("qp_num = %#x", ch->qp->qp_num); + pr_debug("qp_num = %#x\n", ch->qp->qp_num); if (!sdev->use_srq) for (i = 0; i < ch->rq_size; i++) @@ -2284,7 +2239,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr); - TRACE_DBG("%s: max_cqe= %d max_sge= %d sq_size = %d ch= %p", __func__, + pr_debug("%s: max_cqe= %d max_sge= %d sq_size = %d ch= %p\n", __func__, ch->cq->cqe, qp_init->cap.max_send_sge, qp_init->cap.max_send_wr, ch); @@ -2324,13 +2279,13 @@ static bool srpt_close_ch(struct srpt_rdma_ch *ch) ret = srpt_ch_qp_err(ch); if (ret < 0) - PRINT_ERROR("%s-%d: changing queue pair into error state" - " failed: %d", ch->sess_name, ch->qp->qp_num, ret); + pr_err("%s-%d: changing queue pair into error state failed: %d\n", + ch->sess_name, ch->qp->qp_num, ret); ret = srpt_zerolength_write(ch); if (ret < 0) { - PRINT_ERROR("%s-%d: queuing zero-length write failed: %d", - ch->sess_name, ch->qp->qp_num, ret); + pr_err("%s-%d: queuing zero-length write failed: %d\n", + ch->sess_name, ch->qp->qp_num, ret); WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); } @@ -2346,7 +2301,7 @@ static bool srpt_close_ch(struct srpt_rdma_ch *ch) * the responsibility of the caller to ensure that this function is not * invoked concurrently with the code that accepts a connection. This means * that this function must either be invoked from inside a CM callback - * function or that it must be invoked with the srpt_tgt.mutex held. + * function or that it must be invoked with the srpt_port.mutex held. */ static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) { @@ -2369,66 +2324,37 @@ static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) return ret; } -static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) +static void __srpt_close_all_ch(struct srpt_port *sport) { struct srpt_nexus *nexus; struct srpt_rdma_ch *ch; - lockdep_assert_held(&srpt_tgt->mutex); + lockdep_assert_held(&sport->mutex); - list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { + list_for_each_entry(nexus, &sport->nexus_list, entry) { list_for_each_entry(ch, &nexus->ch_list, list) { if (srpt_disconnect_ch(ch) >= 0) - PRINT_INFO("Closing channel %s-%d because" - " target %s has been disabled", - ch->sess_name, ch->qp->qp_num, - srpt_tgt->scst_tgt->tgt_name); + pr_info("Closing channel %s-%d because target %s has been disabled\n", + ch->sess_name, ch->qp->qp_num, + sport->scst_tgt->tgt_name); srpt_close_ch(ch); } } } -static struct srpt_device *srpt_convert_to_sdev(struct scst_tgt *scst_tgt) -{ - struct srpt_port *sport; - - if (one_target_per_port) { - sport = scst_tgt_get_tgt_priv(scst_tgt); - return sport ? sport->sdev : NULL; - } else { - return scst_tgt_get_tgt_priv(scst_tgt); - } -} - -static struct srpt_tgt *srpt_convert_scst_tgt(struct scst_tgt *scst_tgt) -{ - struct srpt_device *sdev; - struct srpt_port *sport; - struct srpt_tgt *srpt_tgt; - - if (one_target_per_port) { - sport = scst_tgt_get_tgt_priv(scst_tgt); - srpt_tgt = sport ? &sport->srpt_tgt : NULL; - } else { - sdev = scst_tgt_get_tgt_priv(scst_tgt); - srpt_tgt = sdev ? &sdev->srpt_tgt : NULL; - } - return srpt_tgt; -} - /* - * Look up (i_port_id, t_port_id) in srpt_tgt->nexus_list. Create an entry if + * Look up (i_port_id, t_port_id) in sport->nexus_list. Create an entry if * it does not yet exist. */ -static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, +static struct srpt_nexus *srpt_get_nexus(struct srpt_port *sport, const u8 i_port_id[16], const u8 t_port_id[16]) { struct srpt_nexus *nexus = NULL, *tmp_nexus = NULL, *n; for (;;) { - mutex_lock(&srpt_tgt->mutex); - list_for_each_entry(n, &srpt_tgt->nexus_list, entry) { + mutex_lock(&sport->mutex); + list_for_each_entry(n, &sport->nexus_list, entry) { if (memcmp(n->i_port_id, i_port_id, 16) == 0 && memcmp(n->t_port_id, t_port_id, 16) == 0) { nexus = n; @@ -2436,10 +2362,10 @@ static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, } } if (!nexus && tmp_nexus) { - list_add_tail(&tmp_nexus->entry, &srpt_tgt->nexus_list); + list_add_tail(&tmp_nexus->entry, &sport->nexus_list); swap(nexus, tmp_nexus); } - mutex_unlock(&srpt_tgt->mutex); + mutex_unlock(&sport->mutex); if (nexus) break; @@ -2464,22 +2390,22 @@ static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, */ static int srpt_enable_target(struct scst_tgt *scst_tgt, bool enable) { - struct srpt_tgt *srpt_tgt = srpt_convert_scst_tgt(scst_tgt); + struct srpt_port *sport = scst_tgt_get_tgt_priv(scst_tgt); int res = -E_TGT_PRIV_NOT_YET_SET; EXTRACHECKS_WARN_ON_ONCE(irqs_disabled()); - if (!srpt_tgt) + if (!sport) goto out; - PRINT_INFO("%s target %s", enable ? "Enabling" : "Disabling", - scst_tgt->tgt_name); + pr_info("%s target %s\n", enable ? "Enabling" : "Disabling", + scst_tgt->tgt_name); - mutex_lock(&srpt_tgt->mutex); - srpt_tgt->enabled = enable; + mutex_lock(&sport->mutex); + sport->enabled = enable; if (!enable) - __srpt_close_all_ch(srpt_tgt); - mutex_unlock(&srpt_tgt->mutex); + __srpt_close_all_ch(sport); + mutex_unlock(&sport->mutex); res = 0; @@ -2492,28 +2418,28 @@ out: */ static bool srpt_is_target_enabled(struct scst_tgt *scst_tgt) { - struct srpt_tgt *srpt_tgt = srpt_convert_scst_tgt(scst_tgt); + struct srpt_port *sport = scst_tgt_get_tgt_priv(scst_tgt); - return srpt_tgt && srpt_tgt->enabled; + return sport && sport->enabled; } #endif /* CONFIG_SCST_PROC */ /* - * srpt_next_comp_vector() - Next completion vector >= srpt_tgt->comp_vector + * srpt_next_comp_vector() - Next completion vector >= sport->comp_vector */ -static u8 srpt_next_comp_vector(struct srpt_tgt *srpt_tgt) +static u8 srpt_next_comp_vector(struct srpt_port *sport) { u8 comp_vector; - mutex_lock(&srpt_tgt->mutex); - comp_vector = find_next_bit(srpt_tgt->comp_v_mask, COMP_V_MASK_SIZE, - srpt_tgt->comp_vector); + mutex_lock(&sport->mutex); + comp_vector = find_next_bit(sport->comp_v_mask, COMP_V_MASK_SIZE, + sport->comp_vector); if (comp_vector >= COMP_V_MASK_SIZE) - comp_vector = find_next_bit(srpt_tgt->comp_v_mask, + comp_vector = find_next_bit(sport->comp_v_mask, COMP_V_MASK_SIZE, 0); sBUG_ON(comp_vector >= COMP_V_MASK_SIZE); - srpt_tgt->comp_vector = comp_vector + 1; - mutex_unlock(&srpt_tgt->mutex); + sport->comp_vector = comp_vector + 1; + mutex_unlock(&sport->mutex); return comp_vector; } @@ -2533,8 +2459,6 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, { struct srpt_port *const sport = &sdev->port[port_num - 1]; const __be16 *const raw_port_gid = (__be16 *)sport->gid.raw; - struct srpt_tgt *const srpt_tgt = one_target_per_port ? - &sport->srpt_tgt : &sdev->srpt_tgt; struct srpt_nexus *nexus; struct srp_login_rsp *rsp = NULL; struct srp_login_rej *rej = NULL; @@ -2561,12 +2485,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, it_iu_len = be32_to_cpu(req->req_it_iu_len); - PRINT_INFO("Received SRP_LOGIN_REQ with i_port_id" - " %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x," - " t_port_id %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x and" - " it_iu_len %d on port %d" - " (guid=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x);" - " pkey %#04x", + pr_info("Received SRP_LOGIN_REQ with i_port_id %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x, t_port_id %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x and it_iu_len %d on port %d (guid=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x); pkey %#04x\n", be16_to_cpu(*(__be16 *)&req->initiator_port_id[0]), be16_to_cpu(*(__be16 *)&req->initiator_port_id[2]), be16_to_cpu(*(__be16 *)&req->initiator_port_id[4]), @@ -2595,7 +2514,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, be16_to_cpu(raw_port_gid[7]), be16_to_cpu(pkey)); - nexus = srpt_get_nexus(srpt_tgt, req->initiator_port_id, + nexus = srpt_get_nexus(sport, req->initiator_port_id, req->target_port_id); if (IS_ERR(nexus)) { ret = PTR_ERR(nexus); @@ -2613,17 +2532,15 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, if (it_iu_len > srp_max_req_size || it_iu_len < 64) { rej->reason = cpu_to_be32( SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE); - PRINT_ERROR("rejected SRP_LOGIN_REQ because its" - " length (%d bytes) is out of range (%d .. %d)", - it_iu_len, 64, srp_max_req_size); + pr_err("rejected SRP_LOGIN_REQ because its length (%d bytes) is out of range (%d .. %d)\n", + it_iu_len, 64, srp_max_req_size); goto reject; } - if (!srpt_tgt->enabled) { + if (!sport->enabled) { rej->reason = cpu_to_be32( SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - PRINT_INFO("rejected SRP_LOGIN_REQ because target %s is not" - " enabled", srpt_tgt->scst_tgt->tgt_name); + pr_info("rejected SRP_LOGIN_REQ because target %s is not enabled\n", sport->scst_tgt->tgt_name); goto reject; } @@ -2632,8 +2549,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, cpu_to_be64(srpt_service_guid)) { rej->reason = cpu_to_be32( SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL); - PRINT_ERROR("rejected SRP_LOGIN_REQ because it" - " has an invalid target port identifier."); + pr_err("rejected SRP_LOGIN_REQ because it has an invalid target port identifier.\n"); goto reject; } @@ -2641,7 +2557,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, ch = kzalloc(sizeof(*ch), GFP_KERNEL); if (!ch) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - PRINT_ERROR("rejected SRP_LOGIN_REQ because out of memory."); + pr_err("rejected SRP_LOGIN_REQ because out of memory.\n"); goto reject; } @@ -2649,7 +2565,6 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, ch->pkey = be16_to_cpu(pkey); ch->nexus = nexus; ch->sport = sport; - ch->srpt_tgt = srpt_tgt; if (ib_cm_id) { ch->ib_cm.cm_id = ib_cm_id; ib_cm_id->context = ch; @@ -2672,8 +2587,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, sizeof(*ch->ioctx_ring[0]), ch->max_rsp_size, 0, DMA_TO_DEVICE); if (!ch->ioctx_ring) { - PRINT_ERROR("rejected SRP_LOGIN_REQ because creating" - " a new QP SQ ring failed."); + pr_err("rejected SRP_LOGIN_REQ because creating a new QP SQ ring failed.\n"); rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); goto free_ch; } @@ -2691,8 +2605,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, DATA_ALIGNMENT_OFFSET, DMA_FROM_DEVICE); if (!ch->ioctx_recv_ring) { - PRINT_ERROR("rejected SRP_LOGIN_REQ because creating" - " a new QP RQ ring failed."); + pr_err("rejected SRP_LOGIN_REQ because creating a new QP RQ ring failed.\n"); rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); goto free_ring; @@ -2701,51 +2614,25 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, INIT_LIST_HEAD(&ch->ioctx_recv_ring[i]->wait_list); } - ch->comp_vector = srpt_next_comp_vector(srpt_tgt); + ch->comp_vector = srpt_next_comp_vector(sport); ret = srpt_create_ch_ib(ch); if (ret) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - PRINT_ERROR("rejected SRP_LOGIN_REQ because creating" - " a new RDMA channel failed."); + pr_err("rejected SRP_LOGIN_REQ because creating a new RDMA channel failed.\n"); goto free_recv_ring; } - if (one_target_per_port) { - strlcpy(ch->sess_name, src_addr, sizeof(ch->sess_name)); - } else if (use_port_guid_in_session_name) { - /* - * If the kernel module parameter use_port_guid_in_session_name - * has been specified, use a combination of the target port - * GUID and the initiator port ID as the session name. This - * was the original behavior of the SRP target implementation - * (i.e. before the SRPT was included in OFED 1.3). - */ - snprintf(ch->sess_name, sizeof(ch->sess_name), - "0x%016llx%016llx", - be64_to_cpu(*(__be64 *) - &sdev->port[port_num - 1].gid.raw[8]), - be64_to_cpu(*(__be64 *)(nexus->i_port_id + 8))); - } else { - /* - * Default behavior: use the initiator port identifier as the - * session name. - */ - snprintf(ch->sess_name, sizeof(ch->sess_name), - "0x%016llx%016llx", - be64_to_cpu(*(__be64 *)nexus->i_port_id), - be64_to_cpu(*(__be64 *)(nexus->i_port_id + 8))); - } + strlcpy(ch->sess_name, src_addr, sizeof(ch->sess_name)); + pr_debug("registering session %s\n", ch->sess_name); - TRACE_DBG("registering session %s", ch->sess_name); - - BUG_ON(!srpt_tgt->scst_tgt); + BUG_ON(!sport->scst_tgt); ret = -ENOMEM; - ch->scst_sess = scst_register_session(srpt_tgt->scst_tgt, 0, - ch->sess_name, ch, NULL, NULL); - if (!ch->scst_sess) { + ch->sess = scst_register_session(sport->scst_tgt, 0, + ch->sess_name, ch, NULL, NULL); + if (!ch->sess) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - TRACE_DBG("Failed to create SCST session"); + pr_debug("Failed to create SCST session\n"); goto destroy_ib; } @@ -2754,11 +2641,11 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, if (IS_ERR(thread)) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); ret = PTR_ERR(thread); - PRINT_ERROR("failed to create kernel thread: %d", ret); + pr_err("failed to create kernel thread: %d\n", ret); goto unreg_ch; } - mutex_lock(&srpt_tgt->mutex); + mutex_lock(&sport->mutex); if ((req->req_flags & SRP_MTCH_ACTION) == SRP_MULTICHAN_SINGLE) { struct srpt_rdma_ch *ch2; @@ -2767,8 +2654,8 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, list_for_each_entry(ch2, &nexus->ch_list, list) { if (srpt_disconnect_ch(ch2) < 0) continue; - PRINT_INFO("Relogin - closed existing channel %s", - ch2->sess_name); + pr_info("Relogin - closed existing channel %s\n", + ch2->sess_name); rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_TERMINATED; } } else { @@ -2778,28 +2665,27 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, list_add_tail(&ch->list, &nexus->ch_list); ch->thread = thread; - if (!srpt_tgt->enabled) { + if (!sport->enabled) { rej->reason = cpu_to_be32( SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - PRINT_INFO("rejected SRP_LOGIN_REQ because the target %s (%s)" - " is not enabled", - srpt_tgt->scst_tgt->tgt_name, sdev->device->name); - mutex_unlock(&srpt_tgt->mutex); + pr_info("rejected SRP_LOGIN_REQ because the target %s (%s) is not enabled\n", + sport->scst_tgt->tgt_name, sdev->device->name); + mutex_unlock(&sport->mutex); goto reject; } - mutex_unlock(&srpt_tgt->mutex); + mutex_unlock(&sport->mutex); ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rtr(ch, ch->qp); if (ret) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - PRINT_ERROR("rejected SRP_LOGIN_REQ because enabling" - " RTR failed (error code = %d)", ret); + pr_err("rejected SRP_LOGIN_REQ because enabling RTR failed (error code = %d)\n", + ret); goto reject; } - TRACE_DBG("Establish connection sess=%p name=%s ch=%p", - ch->scst_sess, ch->sess_name, ch); + pr_debug("Establish connection sess=%p name=%s ch=%p\n", ch->sess, + ch->sess_name, ch); /* create srp_login_response */ rsp->opcode = SRP_LOGIN_RSP; @@ -2835,11 +2721,11 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, } /* - * Hold the srpt_tgt mutex while accepting a connection to avoid that + * Hold the sport mutex while accepting a connection to avoid that * srpt_disconnect_ch() is invoked concurrently with this code. */ - mutex_lock(&srpt_tgt->mutex); - if (srpt_tgt->enabled && ch->state == CH_CONNECTING) { + mutex_lock(&sport->mutex); + if (sport->enabled && ch->state == CH_CONNECTING) { if (ch->using_rdma_cm) ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); else @@ -2847,7 +2733,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, } else { ret = -EINVAL; } - mutex_unlock(&srpt_tgt->mutex); + mutex_unlock(&sport->mutex); switch (ret) { case 0: @@ -2856,15 +2742,14 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, goto reject; default: rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - PRINT_ERROR("sending SRP_LOGIN_REQ response failed" - " (error code = %d)", ret); + pr_err("sending SRP_LOGIN_REQ response failed (error code = %d)\n", ret); goto reject; } goto out; unreg_ch: - scst_unregister_session(ch->scst_sess, true, NULL); + scst_unregister_session(ch->sess, true, NULL); destroy_ib: srpt_destroy_ch_ib(ch); @@ -2890,7 +2775,7 @@ free_ch: BUG_ON(ret == 0); reject: - PRINT_INFO("Rejecting login with reason %#x", be32_to_cpu(rej->reason)); + pr_info("Rejecting login with reason %#x\n", be32_to_cpu(rej->reason)); rej->opcode = SRP_LOGIN_REJ; rej->tag = req->tag; rej->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | @@ -2999,8 +2884,8 @@ static void srpt_cm_rej_recv(struct srpt_rdma_ch *ch, for (i = 0; i < private_data_len; i++) sprintf(priv + 3 * i, "%02x ", private_data[i]); } - PRINT_INFO("Received CM REJ for ch %s-%d; reason %d; private data %s.", - ch->sess_name, ch->qp->qp_num, reason, priv ? : "(?)"); + pr_info("Received CM REJ for ch %s-%d; reason %d; private data %s.\n", + ch->sess_name, ch->qp->qp_num, reason, priv ? : "(?)"); kfree(priv); } @@ -3012,7 +2897,7 @@ static void srpt_check_timeout(struct srpt_rdma_ch *ch) uint32_t T_tr_ms; if (ib_query_qp(ch->qp, &attr, IB_QP_TIMEOUT, &iattr) < 0) { - PRINT_ERROR("Querying QP attributes failed"); + pr_err("Querying QP attributes failed\n"); return; } @@ -3028,16 +2913,13 @@ static void srpt_check_timeout(struct srpt_rdma_ch *ch) do_div(max_compl_time_ms, 1000000); T_tr_ms = T_tr_ns; do_div(T_tr_ms, 1000000); - TRACE_DBG("%s-%d: QP local ack timeout = %d or T_tr =" - " %u ms; retry_cnt = %d; max compl. time = %d ms", + pr_debug("%s-%d: QP local ack timeout = %d or T_tr = %u ms; retry_cnt = %d; max compl. time = %d ms\n", ch->sess_name, ch->qp->qp_num, attr.timeout, T_tr_ms, attr.retry_cnt, (unsigned)max_compl_time_ms); if (max_compl_time_ms >= RDMA_COMPL_TIMEOUT_S * 1000) { - PRINT_ERROR("Maximum RDMA completion time (%d ms)" - " exceeds ib_srpt timeout (%d ms)", - (unsigned)max_compl_time_ms, - 1000 * RDMA_COMPL_TIMEOUT_S); + pr_err("Maximum RDMA completion time (%lld ms) exceeds ib_srpt timeout (%d ms)\n", + max_compl_time_ms, 1000 * RDMA_COMPL_TIMEOUT_S); } } } @@ -3054,8 +2936,8 @@ static void srpt_cm_rtu_recv(struct srpt_rdma_ch *ch) ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rts(ch, ch->qp); if (ret < 0) { - PRINT_ERROR("%s-%d: QP transition to RTS failed", - ch->sess_name, ch->qp->qp_num); + pr_err("%s-%d: QP transition to RTS failed\n", ch->sess_name, + ch->qp->qp_num); srpt_close_ch(ch); return; } @@ -3068,21 +2950,21 @@ static void srpt_cm_rtu_recv(struct srpt_rdma_ch *ch) * already been invoked from another thread. */ if (!srpt_set_ch_state(ch, CH_LIVE)) - PRINT_ERROR("%s-%d: channel transition to LIVE state failed", - ch->sess_name, ch->qp->qp_num); + pr_err("%s-%d: channel transition to LIVE state failed\n", + ch->sess_name, ch->qp->qp_num); } static void srpt_cm_timewait_exit(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received CM TimeWait exit for ch %s-%d.", ch->sess_name, - ch->qp->qp_num); + pr_info("Received CM TimeWait exit for ch %s-%d.\n", ch->sess_name, + ch->qp->qp_num); srpt_close_ch(ch); } static void srpt_cm_rep_error(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received CM REP error for ch %s-%d.", ch->sess_name, - ch->qp->qp_num); + pr_info("Received CM REP error for ch %s-%d.\n", ch->sess_name, + ch->qp->qp_num); } /** @@ -3099,8 +2981,8 @@ static int srpt_cm_dreq_recv(struct srpt_rdma_ch *ch) */ static void srpt_cm_drep_recv(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received CM DREP message for ch %s-%d.", ch->sess_name, - ch->qp->qp_num); + pr_info("Received CM DREP message for ch %s-%d.\n", ch->sess_name, + ch->qp->qp_num); srpt_close_ch(ch); } @@ -3149,14 +3031,13 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) srpt_cm_rep_error(ch); break; case IB_CM_DREQ_ERROR: - PRINT_INFO("Received CM DREQ ERROR event."); + pr_info("Received CM DREQ ERROR event.\n"); break; case IB_CM_MRA_RECEIVED: - PRINT_INFO("Received CM MRA event"); + pr_info("Received CM MRA event\n"); break; default: - PRINT_ERROR("received unrecognized IB CM event %d", - event->event); + pr_err("received unrecognized IB CM event %d\n", event->event); break; } @@ -3197,8 +3078,8 @@ static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id, case RDMA_CM_EVENT_ADDR_CHANGE: break; default: - PRINT_ERROR("received unrecognized RDMA CM event %d", - event->event); + pr_err("received unrecognized RDMA CM event %d\n", + event->event); break; } @@ -3210,7 +3091,7 @@ static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id, */ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, struct srpt_send_ioctx *ioctx, - struct scst_cmd *scmnd) + struct scst_cmd *cmd) { struct ib_device *dev; struct scatterlist *sg, *cur_sg; @@ -3230,22 +3111,22 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, BUG_ON(!ch); BUG_ON(!ioctx); - BUG_ON(!scmnd); + BUG_ON(!cmd); dev = ch->sport->sdev->device; max_sge = ch->max_sge; - dir = scst_cmd_get_data_direction(scmnd); + dir = scst_cmd_get_data_direction(cmd); BUG_ON(dir == SCST_DATA_NONE); /* * Cache 'dir' because it is needed in srpt_unmap_sg_to_ib_sge() - * and because scst_set_cmd_error_status() resets scmnd->data_direction. + * and because scst_set_cmd_error_status() resets cmd->data_direction. */ ioctx->dir = dir; if (dir == SCST_DATA_WRITE) { - scst_cmd_get_write_fields(scmnd, &sg, &sg_cnt); + scst_cmd_get_write_fields(cmd, &sg, &sg_cnt); WARN_ON(!sg); } else { - sg = scst_cmd_get_sg(scmnd); - sg_cnt = scst_cmd_get_sg_cnt(scmnd); + sg = scst_cmd_get_sg(cmd); + sg_cnt = scst_cmd_get_sg_cnt(cmd); WARN_ON(!sg); } ioctx->sg = sg; @@ -3265,7 +3146,7 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, size = nrdma * sizeof(*riu) + nsge * sizeof(*sge); ioctx->rdma_ius = size <= sizeof(ioctx->rdma_ius_buf) ? ioctx->rdma_ius_buf : kmalloc(size, - scst_cmd_atomic(scmnd) ? GFP_ATOMIC : GFP_KERNEL); + scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL); if (!ioctx->rdma_ius) goto free_mem; @@ -3275,8 +3156,8 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, db = ioctx->rbufs; tsize = (dir == SCST_DATA_READ) - ? scst_cmd_get_adjusted_resp_data_len(scmnd) - : scst_cmd_get_bufflen(scmnd); + ? scst_cmd_get_adjusted_resp_data_len(cmd) + : scst_cmd_get_bufflen(cmd); dma_len = ib_sg_dma_len(dev, &sg[0]); riu = ioctx->rdma_ius; sge = sge_array; @@ -3338,8 +3219,8 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, db = ioctx->rbufs; tsize = (dir == SCST_DATA_READ) - ? scst_cmd_get_adjusted_resp_data_len(scmnd) - : scst_cmd_get_bufflen(scmnd); + ? scst_cmd_get_adjusted_resp_data_len(cmd) + : scst_cmd_get_bufflen(cmd); riu = ioctx->rdma_ius; dma_len = ib_sg_dma_len(dev, &sg[0]); dma_addr = ib_sg_dma_address(dev, &sg[0]); @@ -3422,7 +3303,7 @@ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch, if (ioctx->mapped_sg_count) { EXTRACHECKS_WARN_ON(ioctx - != scst_cmd_get_tgt_priv(&ioctx->scmnd)); + != scst_cmd_get_tgt_priv(&ioctx->cmd)); sg = ioctx->sg; EXTRACHECKS_WARN_ON(!sg); dir = ioctx->dir; @@ -3454,8 +3335,8 @@ static int srpt_perform_rdmas(struct srpt_rdma_ch *ch, ret = -ENOMEM; sq_wr_avail = srpt_adjust_sq_wr_avail(ch, -n_rdma); if (sq_wr_avail < 0) { - PRINT_WARNING("ch %s-%d send queue full (needed %d)", - ch->sess_name, ch->qp->qp_num, n_rdma); + pr_warn("ch %s-%d send queue full (needed %d)\n", + ch->sess_name, ch->qp->qp_num, n_rdma); goto out; } } @@ -3495,28 +3376,28 @@ static int srpt_perform_rdmas(struct srpt_rdma_ch *ch, } if (ret) - PRINT_ERROR("%s[%d]: ib_post_send() returned %d for %d/%d", - __func__, __LINE__, ret, i, n_rdma); + pr_err("%s: ib_post_send() returned %d for %d/%d\n", __func__, + ret, i, n_rdma); if (ret && i > 0) { wr.num_sge = 0; wr.wr_id = encode_wr_id(SRPT_RDMA_ABORT, ioctx->ioctx.index); wr.send_flags = IB_SEND_SIGNALED; - PRINT_INFO("Trying to abort failed RDMA transfer [%d]", - ioctx->ioctx.index); + pr_info("Trying to abort failed RDMA transfer [%d]\n", + ioctx->ioctx.index); while (ch->state == CH_LIVE && ib_post_send(ch->qp, &wr, &bad_wr) != 0) { - PRINT_INFO("Trying to abort failed RDMA transfer [%d]", - ioctx->ioctx.index); + pr_info("Trying to abort failed RDMA transfer [%d]\n", + ioctx->ioctx.index); msleep(1000); } - PRINT_INFO("Waiting until RDMA abort finished [%d]", - ioctx->ioctx.index); + pr_info("Waiting until RDMA abort finished [%d]\n", + ioctx->ioctx.index); while (ch->state < CH_DISCONNECTED && !ioctx->rdma_aborted) { - PRINT_INFO("Waiting until RDMA abort finished [%d]", - ioctx->ioctx.index); + pr_info("Waiting until RDMA abort finished [%d]\n", + ioctx->ioctx.index); msleep(1000); } - PRINT_INFO("%s[%d]: done", __func__, __LINE__); + pr_info("%s[%d]: done\n", __func__, __LINE__); } out: @@ -3528,62 +3409,60 @@ out: /** * srpt_xfer_data() - Start data transfer from initiator to target. * - * Returns an SCST_TGT_RES_... status code. + * Returns 0, -EAGAIN or -EIO. * * Note: Must not block. */ static int srpt_xfer_data(struct srpt_rdma_ch *ch, - struct srpt_send_ioctx *ioctx, - struct scst_cmd *scmnd) + struct srpt_send_ioctx *ioctx) { + struct scst_cmd *cmd = &ioctx->cmd; int ret; if (ioctx->imm_data) { BUG_ON(!srpt_test_and_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA, SRPT_STATE_DATA_IN)); - if (unlikely(!scst_cmd_get_tgt_data_buff_alloced(scmnd))) { + if (unlikely(!scst_cmd_get_tgt_data_buff_alloced(cmd))) { unsigned offset = 0, len; uint8_t *buf; - len = scst_get_buf_first(scmnd, &buf); + len = scst_get_buf_first(cmd, &buf); while (len > 0) { memcpy(buf, ioctx->imm_data + offset, len); offset += len; - len = scst_get_buf_next(scmnd, &buf); + len = scst_get_buf_next(cmd, &buf); } WARN_ON_ONCE(offset != - scst_cmd_get_expected_transfer_len(scmnd)); + scst_cmd_get_expected_transfer_len_full(cmd)); } - scst_rx_data(scmnd, SCST_RX_STATUS_SUCCESS, + scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS, in_irq() ? SCST_CONTEXT_TASKLET : in_softirq() ? SCST_CONTEXT_DIRECT_ATOMIC : SCST_CONTEXT_DIRECT); - ret = SCST_TGT_RES_SUCCESS; + ret = 0; goto out; } - ret = srpt_map_sg_to_ib_sge(ch, ioctx, scmnd); + ret = srpt_map_sg_to_ib_sge(ch, ioctx, cmd); if (ret) { - PRINT_ERROR("%s[%d] ret=%d", __func__, __LINE__, ret); - ret = SCST_TGT_RES_QUEUE_FULL; + pr_err("%s srpt_map_sg_to_ib_sge() ret=%d\n", __func__, ret); + ret = -EAGAIN; goto out; } - ret = srpt_perform_rdmas(ch, ioctx, scst_cmd_get_data_direction(scmnd)); + ret = srpt_perform_rdmas(ch, ioctx, scst_cmd_get_data_direction(cmd)); if (ret) { if (ret == -EAGAIN || ret == -ENOMEM) { - PRINT_INFO("%s[%d] queue full -- ret=%d", - __func__, __LINE__, ret); - ret = SCST_TGT_RES_QUEUE_FULL; + pr_info("%s: queue full -- ret=%d\n", __func__, ret); + ret = -EAGAIN; } else { - PRINT_ERROR("%s[%d] fatal error -- ret=%d", - __func__, __LINE__, ret); - ret = SCST_TGT_RES_FATAL_ERROR; + pr_err("%s: fatal error -- ret=%d\n", __func__, ret); + ret = -EIO; } goto out_unmap; } - ret = SCST_TGT_RES_SUCCESS; + ret = 0; out: return ret; @@ -3598,12 +3477,12 @@ out_unmap: * Called by the SCST core if no IB completion notification has been received * within RDMA_COMPL_TIMEOUT_S seconds. */ -static void srpt_pending_cmd_timeout(struct scst_cmd *scmnd) +static void srpt_pending_cmd_timeout(struct scst_cmd *cmd) { struct srpt_send_ioctx *ioctx; enum srpt_command_state state; - ioctx = scst_cmd_get_tgt_priv(scmnd); + ioctx = scst_cmd_get_tgt_priv(cmd); BUG_ON(!ioctx); state = ioctx->state; @@ -3615,16 +3494,15 @@ static void srpt_pending_cmd_timeout(struct scst_cmd *scmnd) * srpt_pending_cmd_timeout() should never be invoked for * commands in this state. */ - PRINT_ERROR("Processing SCST command %p (SRPT state %d) took" - " too long -- aborting", scmnd, state); + pr_err("Processing SCST command %p (SRPT state %d) took too long -- aborting\n", + cmd, state); break; case SRPT_STATE_NEED_DATA: case SRPT_STATE_CMD_RSP_SENT: case SRPT_STATE_MGMT_RSP_SENT: default: - PRINT_ERROR("Command %p: IB completion for idx %u has not" - " been received in time (SRPT command state %d)", - scmnd, ioctx->ioctx.index, state); + pr_err("Command %p: IB completion for idx %u has not been received in time (SRPT command state %d)\n", + cmd, ioctx->ioctx.index, state); break; } @@ -3637,19 +3515,25 @@ static void srpt_pending_cmd_timeout(struct scst_cmd *scmnd) * Called by the SCST core to transfer data from the initiator to the target * (SCST_DATA_WRITE). Must not block. */ -static int srpt_rdy_to_xfer(struct scst_cmd *scmnd) +static int srpt_rdy_to_xfer(struct scst_cmd *cmd) { - struct srpt_send_ioctx *ioctx; + struct srpt_send_ioctx *ioctx = scst_cmd_get_tgt_priv(cmd); enum srpt_command_state prev_cmd_state; int ret; - ioctx = scst_cmd_get_tgt_priv(scmnd); prev_cmd_state = srpt_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA); - ret = srpt_xfer_data(ioctx->ch, ioctx, scmnd); - if (unlikely(ret != SCST_TGT_RES_SUCCESS)) - srpt_set_cmd_state(ioctx, prev_cmd_state); + ret = srpt_xfer_data(ioctx->ch, ioctx); - return ret; + switch (ret) { + case 0: + return SCST_TGT_RES_SUCCESS; + case -EAGAIN: + srpt_set_cmd_state(ioctx, prev_cmd_state); + return SCST_TGT_RES_QUEUE_FULL; + default: + srpt_set_cmd_state(ioctx, prev_cmd_state); + return SCST_TGT_RES_FATAL_ERROR; + } } /** @@ -3658,7 +3542,7 @@ static int srpt_rdy_to_xfer(struct scst_cmd *scmnd) * Callback function called by the SCST core. Must not block. Must ensure that * scst_tgt_cmd_done() will get invoked when returning SCST_TGT_RES_SUCCESS. */ -static int srpt_xmit_response(struct scst_cmd *scmnd) +static int srpt_xmit_response(struct scst_cmd *cmd) { struct srpt_rdma_ch *ch; struct srpt_send_ioctx *ioctx; @@ -3669,10 +3553,10 @@ static int srpt_xmit_response(struct scst_cmd *scmnd) ret = SCST_TGT_RES_SUCCESS; - ioctx = scst_cmd_get_tgt_priv(scmnd); + ioctx = scst_cmd_get_tgt_priv(cmd); BUG_ON(!ioctx); - ch = scst_sess_get_tgt_priv(scst_cmd_get_session(scmnd)); + ch = scst_sess_get_tgt_priv(scst_cmd_get_session(cmd)); BUG_ON(!ch); state = ioctx->state; @@ -3686,43 +3570,43 @@ static int srpt_xmit_response(struct scst_cmd *scmnd) break; } - if (unlikely(scst_cmd_aborted_on_xmit(scmnd))) { + if (unlikely(scst_cmd_aborted_on_xmit(cmd))) { srpt_adjust_req_lim(ch, 0, 1); srpt_abort_cmd(ioctx, SCST_CONTEXT_SAME); goto out; } - EXTRACHECKS_BUG_ON(scst_cmd_atomic(scmnd)); + EXTRACHECKS_BUG_ON(scst_cmd_atomic(cmd)); - dir = scst_cmd_get_data_direction(scmnd); + dir = scst_cmd_get_data_direction(cmd); /* For read commands, transfer the data to the initiator. */ if (dir == SCST_DATA_READ - && scst_cmd_get_adjusted_resp_data_len(scmnd)) { - ret = srpt_xfer_data(ch, ioctx, scmnd); + && scst_cmd_get_adjusted_resp_data_len(cmd)) { + ret = srpt_xfer_data(ch, ioctx); if (unlikely(ret != SCST_TGT_RES_SUCCESS)) { srpt_set_cmd_state(ioctx, state); - PRINT_WARNING("xfer_data failed for tag %llu" - " - %s", scst_cmd_get_tag(scmnd), - ret == SCST_TGT_RES_QUEUE_FULL ? - "retrying" : "failing"); + pr_warn("xfer_data failed for tag %llu - %s\n", + scst_cmd_get_tag(cmd), + ret == SCST_TGT_RES_QUEUE_FULL ? "retrying" : + "failing"); goto out; } } ioctx->req_lim_delta = srpt_inc_req_lim(ch); resp_len = srpt_build_cmd_rsp(ch, ioctx, - scst_cmd_get_tag(scmnd), - scst_cmd_get_status(scmnd), - scst_cmd_get_sense_buffer(scmnd), - scst_cmd_get_sense_buffer_len(scmnd)); + scst_cmd_get_tag(cmd), + scst_cmd_get_status(cmd), + scst_cmd_get_sense_buffer(cmd), + scst_cmd_get_sense_buffer_len(cmd)); if (srpt_post_send(ch, ioctx, resp_len)) { srpt_unmap_sg_to_ib_sge(ch, ioctx); srpt_set_cmd_state(ioctx, state); srpt_undo_inc_req_lim(ch, ioctx->req_lim_delta); - PRINT_WARNING("sending response failed for tag %llu - retrying", - scst_cmd_get_tag(scmnd)); + pr_warn("sending response failed for tag %llu - retrying\n", + scst_cmd_get_tag(cmd)); ret = SCST_TGT_RES_QUEUE_FULL; } @@ -3748,9 +3632,8 @@ static void srpt_tsk_mgmt_done(struct scst_mgmt_cmd *mcmnd) ch = ioctx->ch; BUG_ON(!ch); - TRACE_DBG("%s: tsk_mgmt_done for tag= %lld status=%d", - __func__, ioctx->tsk_mgmt.tag, - scst_mgmt_cmd_get_status(mcmnd)); + pr_debug("tsk_mgmt_done for tag= %lld status=%d\n", ioctx->tsk_mgmt.tag, + scst_mgmt_cmd_get_status(mcmnd)); WARN_ON(in_irq()); @@ -3769,7 +3652,7 @@ static void srpt_tsk_mgmt_done(struct scst_mgmt_cmd *mcmnd) * response is sent. This is fine however. */ if (srpt_post_send(ch, ioctx, rsp_len)) { - PRINT_ERROR("Sending SRP_RSP response failed."); + pr_err("Sending SRP_RSP response failed.\n"); srpt_put_send_ioctx(ioctx); srpt_undo_inc_req_lim(ch, ioctx->req_lim_delta); } @@ -3781,7 +3664,7 @@ static void srpt_tsk_mgmt_done(struct scst_mgmt_cmd *mcmnd) * See also SPC-3, section 7.5.4.5, TransportID for initiator ports using SRP. */ static int srpt_get_initiator_port_transport_id(struct scst_tgt *tgt, - struct scst_session *scst_sess, uint8_t **transport_id) + struct scst_session *sess, uint8_t **transport_id) { struct srpt_rdma_ch *ch; struct spc_rdma_transport_id { @@ -3790,26 +3673,20 @@ static int srpt_get_initiator_port_transport_id(struct scst_tgt *tgt, uint8_t i_port_id[16]; }; struct spc_rdma_transport_id *tr_id; - int res; + int res = SCSI_TRANSPORTID_PROTOCOLID_SRP; - TRACE_ENTRY(); - - if (!scst_sess) { - res = SCSI_TRANSPORTID_PROTOCOLID_SRP; + if (!sess) goto out; - } - ch = scst_sess_get_tgt_priv(scst_sess); + ch = scst_sess_get_tgt_priv(sess); BUG_ON(!ch); BUILD_BUG_ON(sizeof(*tr_id) != 24); + res = -ENOMEM; tr_id = kzalloc(sizeof(struct spc_rdma_transport_id), GFP_KERNEL); - if (!tr_id) { - PRINT_ERROR("Allocation of TransportID failed"); - res = -ENOMEM; + if (!tr_id) goto out; - } res = 0; tr_id->protocol_identifier = SCSI_TRANSPORTID_PROTOCOLID_SRP; @@ -3819,7 +3696,6 @@ static int srpt_get_initiator_port_transport_id(struct scst_tgt *tgt, *transport_id = (uint8_t *)tr_id; out: - TRACE_EXIT_RES(res); return res; } @@ -3828,11 +3704,11 @@ out: * * Called by the SCST core. May be called in IRQ context. */ -static void srpt_on_free_cmd(struct scst_cmd *scmnd) +static void srpt_on_free_cmd(struct scst_cmd *cmd) { struct srpt_send_ioctx *ioctx; - ioctx = scst_cmd_get_tgt_priv(scmnd); + ioctx = scst_cmd_get_tgt_priv(cmd); srpt_put_send_ioctx(ioctx); } @@ -3852,46 +3728,28 @@ static void srpt_refresh_port_work(struct work_struct *work) srpt_refresh_port(sport); } -/** - * srpt_detect() - Returns the number of target adapters. - * - * Callback function called by the SCST core. - */ -static int srpt_detect(struct scst_tgt_template *tp) -{ - int device_count; - - TRACE_ENTRY(); - - device_count = atomic_read(&srpt_device_count); - - TRACE_EXIT_RES(device_count); - - return device_count; -} - static int srpt_close_session(struct scst_session *sess) { struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(sess); - struct srpt_tgt *srpt_tgt = ch->srpt_tgt; + struct srpt_port *sport = ch->sport; - mutex_lock(&srpt_tgt->mutex); + mutex_lock(&sport->mutex); srpt_disconnect_ch(ch); - mutex_unlock(&srpt_tgt->mutex); + mutex_unlock(&sport->mutex); return 0; } -static bool srpt_ch_list_empty(struct srpt_tgt *srpt_tgt) +static bool srpt_ch_list_empty(struct srpt_port *sport) { struct srpt_nexus *nexus; bool res = true; - mutex_lock(&srpt_tgt->mutex); - list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) + mutex_lock(&sport->mutex); + list_for_each_entry(nexus, &sport->nexus_list, entry) if (!list_empty(&nexus->ch_list)) res = false; - mutex_unlock(&srpt_tgt->mutex); + mutex_unlock(&sport->mutex); return res; } @@ -3899,47 +3757,43 @@ static bool srpt_ch_list_empty(struct srpt_tgt *srpt_tgt) /** * srpt_release_sport() - Free channel resources associated with a target. */ -static int srpt_release_sport(struct srpt_tgt *srpt_tgt) +static int srpt_release_sport(struct srpt_port *sport) { struct srpt_nexus *nexus, *next_n; struct srpt_rdma_ch *ch; - TRACE_ENTRY(); - WARN_ON_ONCE(irqs_disabled()); - BUG_ON(!srpt_tgt); + BUG_ON(!sport); /* Disallow new logins and close all active sessions. */ - mutex_lock(&srpt_tgt->mutex); - srpt_tgt->enabled = false; - __srpt_close_all_ch(srpt_tgt); - mutex_unlock(&srpt_tgt->mutex); + mutex_lock(&sport->mutex); + sport->enabled = false; + __srpt_close_all_ch(sport); + mutex_unlock(&sport->mutex); - while (wait_event_timeout(srpt_tgt->ch_releaseQ, - srpt_ch_list_empty(srpt_tgt), 5 * HZ) <= 0) { - PRINT_INFO("%s: waiting for session unregistration ...", - srpt_tgt->scst_tgt->tgt_name); - mutex_lock(&srpt_tgt->mutex); - list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { + while (wait_event_timeout(sport->ch_releaseQ, + srpt_ch_list_empty(sport), 5 * HZ) <= 0) { + pr_info("%s: waiting for session unregistration ...\n", + sport->scst_tgt->tgt_name); + mutex_lock(&sport->mutex); + list_for_each_entry(nexus, &sport->nexus_list, entry) { list_for_each_entry(ch, &nexus->ch_list, list) { - PRINT_INFO("%s-%d: state %s; %d commands in" - " progress", ch->sess_name, - ch->qp->qp_num, - get_ch_state_name(ch->state), - atomic_read(&ch->scst_sess->sess_cmd_count)); + pr_info("%s-%d: state %s; %d commands in progress\n", + ch->sess_name, ch->qp->qp_num, + get_ch_state_name(ch->state), + atomic_read(&ch->sess->sess_cmd_count)); } } - mutex_unlock(&srpt_tgt->mutex); + mutex_unlock(&sport->mutex); } - mutex_lock(&srpt_tgt->mutex); - list_for_each_entry_safe(nexus, next_n, &srpt_tgt->nexus_list, entry) { + mutex_lock(&sport->mutex); + list_for_each_entry_safe(nexus, next_n, &sport->nexus_list, entry) { list_del(&nexus->entry); kfree(nexus); } - mutex_unlock(&srpt_tgt->mutex); + mutex_unlock(&sport->mutex); - TRACE_EXIT(); return 0; } @@ -3950,21 +3804,17 @@ static int srpt_release_sport(struct srpt_tgt *srpt_tgt) */ static int srpt_release(struct scst_tgt *scst_tgt) { - struct srpt_tgt *srpt_tgt = srpt_convert_scst_tgt(scst_tgt); - - TRACE_ENTRY(); + struct srpt_port *sport = scst_tgt_get_tgt_priv(scst_tgt); EXTRACHECKS_WARN_ON_ONCE(irqs_disabled()); BUG_ON(!scst_tgt); - BUG_ON(!srpt_tgt); + BUG_ON(!sport); - srpt_release_sport(srpt_tgt); + srpt_release_sport(sport); scst_tgt_set_tgt_priv(scst_tgt, NULL); - TRACE_EXIT(); - return 0; } @@ -3984,12 +3834,12 @@ static ssize_t show_comp_v_mask(struct kobject *kobj, { struct scst_tgt *scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); - struct srpt_tgt *srpt_tgt = srpt_convert_scst_tgt(scst_tgt); + struct srpt_port *sport = scst_tgt_get_tgt_priv(scst_tgt); int res = -E_TGT_PRIV_NOT_YET_SET; - if (!srpt_tgt) + if (!sport) goto out; - res = sprintf(buf, "%#lx\n%s", srpt_tgt->comp_v_mask[0], + res = sprintf(buf, "%#lx\n%s", sport->comp_v_mask[0], SCST_SYSFS_KEY_MARK "\n"); out: @@ -4002,13 +3852,14 @@ static ssize_t store_comp_v_mask(struct kobject *kobj, { struct scst_tgt *scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); - struct srpt_tgt *srpt_tgt = srpt_convert_scst_tgt(scst_tgt); - struct srpt_device *sdev = srpt_convert_to_sdev(scst_tgt); + struct srpt_port *sport = scst_tgt_get_tgt_priv(scst_tgt); + struct srpt_device *sdev; int res = -E_TGT_PRIV_NOT_YET_SET; unsigned long mask; - if (!srpt_tgt) + if (!sport) goto out; + sdev = sport->sdev; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) res = kstrtoul(buf, 16, &mask); #else @@ -4019,7 +3870,7 @@ static ssize_t store_comp_v_mask(struct kobject *kobj, res = -EINVAL; if (mask == 0 || mask >= (1ULL << sdev->device->num_comp_vectors)) goto out; - srpt_tgt->comp_v_mask[0] = mask; + sport->comp_v_mask[0] = mask; res = count; out: @@ -4035,21 +3886,14 @@ static ssize_t srpt_show_device(struct kobject *kobj, { struct scst_tgt *scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); - struct srpt_tgt *srpt_tgt = srpt_convert_scst_tgt(scst_tgt); + struct srpt_port *sport = scst_tgt_get_tgt_priv(scst_tgt); struct srpt_device *sdev; int res = -E_TGT_PRIV_NOT_YET_SET; - if (!srpt_tgt) + if (!sport) goto out; - if (one_target_per_port) { - struct srpt_port *sport; - - sport = container_of(srpt_tgt, struct srpt_port, srpt_tgt); - sdev = sport->sdev; - } else { - sdev = container_of(srpt_tgt, struct srpt_device, srpt_tgt); - } + sdev = sport->sdev; res = sprintf(buf, "%s\n", sdev->device->name); out: @@ -4064,54 +3908,26 @@ static ssize_t show_login_info(struct kobject *kobj, { struct scst_tgt *scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); - struct srpt_tgt *srpt_tgt = srpt_convert_scst_tgt(scst_tgt); - struct srpt_port *sport; - int i, res = -E_TGT_PRIV_NOT_YET_SET; + struct srpt_port *sport = scst_tgt_get_tgt_priv(scst_tgt); + int res = -E_TGT_PRIV_NOT_YET_SET; - if (!srpt_tgt) + if (!sport) goto out; - if (one_target_per_port) { - sport = container_of(srpt_tgt, struct srpt_port, srpt_tgt); - res = sprintf(buf, - "tid_ext=%016llx,ioc_guid=%016llx,pkey=ffff," - "dgid=%04x%04x%04x%04x%04x%04x%04x%04x," - "service_id=%016llx\n", - srpt_service_guid, srpt_service_guid, - be16_to_cpu(((__be16 *) sport->gid.raw)[0]), - be16_to_cpu(((__be16 *) sport->gid.raw)[1]), - be16_to_cpu(((__be16 *) sport->gid.raw)[2]), - be16_to_cpu(((__be16 *) sport->gid.raw)[3]), - be16_to_cpu(((__be16 *) sport->gid.raw)[4]), - be16_to_cpu(((__be16 *) sport->gid.raw)[5]), - be16_to_cpu(((__be16 *) sport->gid.raw)[6]), - be16_to_cpu(((__be16 *) sport->gid.raw)[7]), - srpt_service_guid); - } else { - struct srpt_device *sdev; - - sdev = container_of(srpt_tgt, struct srpt_device, srpt_tgt); - res = 0; - for (i = 0; i < sdev->device->phys_port_cnt; i++) { - sport = &sdev->port[i]; - - res += sprintf(buf + res, - "tid_ext=%016llx,ioc_guid=%016llx,pkey=ffff," - "dgid=%04x%04x%04x%04x%04x%04x%04x%04x," - "service_id=%016llx\n", - srpt_service_guid, - srpt_service_guid, - be16_to_cpu(((__be16 *) sport->gid.raw)[0]), - be16_to_cpu(((__be16 *) sport->gid.raw)[1]), - be16_to_cpu(((__be16 *) sport->gid.raw)[2]), - be16_to_cpu(((__be16 *) sport->gid.raw)[3]), - be16_to_cpu(((__be16 *) sport->gid.raw)[4]), - be16_to_cpu(((__be16 *) sport->gid.raw)[5]), - be16_to_cpu(((__be16 *) sport->gid.raw)[6]), - be16_to_cpu(((__be16 *) sport->gid.raw)[7]), - srpt_service_guid); - } - } + res = sprintf(buf, + "tid_ext=%016llx,ioc_guid=%016llx,pkey=ffff," + "dgid=%04x%04x%04x%04x%04x%04x%04x%04x," + "service_id=%016llx\n", + srpt_service_guid, srpt_service_guid, + be16_to_cpu(((__be16 *) sport->gid.raw)[0]), + be16_to_cpu(((__be16 *) sport->gid.raw)[1]), + be16_to_cpu(((__be16 *) sport->gid.raw)[2]), + be16_to_cpu(((__be16 *) sport->gid.raw)[3]), + be16_to_cpu(((__be16 *) sport->gid.raw)[4]), + be16_to_cpu(((__be16 *) sport->gid.raw)[5]), + be16_to_cpu(((__be16 *) sport->gid.raw)[6]), + be16_to_cpu(((__be16 *) sport->gid.raw)[7]), + srpt_service_guid); out: return res; @@ -4130,11 +3946,11 @@ static const struct attribute *srpt_tgt_attrs[] = { static ssize_t show_req_lim(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct scst_session *scst_sess; + struct scst_session *sess; struct srpt_rdma_ch *ch; - scst_sess = container_of(kobj, struct scst_session, sess_kobj); - ch = scst_sess_get_tgt_priv(scst_sess); + sess = container_of(kobj, struct scst_session, sess_kobj); + ch = scst_sess_get_tgt_priv(sess); if (!ch) return -ENOENT; return sprintf(buf, "%d\n", ch->req_lim); @@ -4143,11 +3959,11 @@ static ssize_t show_req_lim(struct kobject *kobj, static ssize_t show_req_lim_delta(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct scst_session *scst_sess; + struct scst_session *sess; struct srpt_rdma_ch *ch; - scst_sess = container_of(kobj, struct scst_session, sess_kobj); - ch = scst_sess_get_tgt_priv(scst_sess); + sess = container_of(kobj, struct scst_session, sess_kobj); + ch = scst_sess_get_tgt_priv(sess); if (!ch) return -ENOENT; return sprintf(buf, "%d\n", ch->req_lim_delta); @@ -4156,11 +3972,11 @@ static ssize_t show_req_lim_delta(struct kobject *kobj, static ssize_t show_ch_state(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct scst_session *scst_sess; + struct scst_session *sess; struct srpt_rdma_ch *ch; - scst_sess = container_of(kobj, struct scst_session, sess_kobj); - ch = scst_sess_get_tgt_priv(scst_sess); + sess = container_of(kobj, struct scst_session, sess_kobj); + ch = scst_sess_get_tgt_priv(sess); if (!ch) return -ENOENT; return sprintf(buf, "%s\n", get_ch_state_name(ch->state)); @@ -4170,11 +3986,11 @@ static ssize_t show_ch_state(struct kobject *kobj, struct kobj_attribute *attr, static ssize_t show_comp_vector(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct scst_session *scst_sess; + struct scst_session *sess; struct srpt_rdma_ch *ch; - scst_sess = container_of(kobj, struct scst_session, sess_kobj); - ch = scst_sess_get_tgt_priv(scst_sess); + sess = container_of(kobj, struct scst_session, sess_kobj); + ch = scst_sess_get_tgt_priv(sess); return ch ? sprintf(buf, "%u\n", ch->comp_vector) : -ENOENT; } #endif @@ -4212,16 +4028,10 @@ static struct scst_tgt_template srpt_template = { .tgt_attrs = srpt_tgt_attrs, .sess_attrs = srpt_sess_attrs, #endif -#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) - .default_trace_flags = DEFAULT_SRPT_TRACE_FLAGS, - .trace_flags = &trace_flag, -#endif - .detect = srpt_detect, .release = srpt_release, .close_session = srpt_close_session, .xmit_response = srpt_xmit_response, .rdy_to_xfer = srpt_rdy_to_xfer, - .on_abort_cmd = srpt_on_abort_cmd, .on_hw_pending_cmd_timeout = srpt_pending_cmd_timeout, .on_free_cmd = srpt_on_free_cmd, .task_mgmt_fn_done = srpt_tsk_mgmt_done, @@ -4252,18 +4062,6 @@ static struct scst_proc_data srpt_log_proc_data = { #endif /* CONFIG_SCST_PROC */ -/* Note: the caller must have zero-initialized *@srpt_tgt. */ -static void srpt_init_tgt(struct srpt_tgt *srpt_tgt) -{ - int i; - - INIT_LIST_HEAD(&srpt_tgt->nexus_list); - init_waitqueue_head(&srpt_tgt->ch_releaseQ); - mutex_init(&srpt_tgt->mutex); - for (i = 0; i < COMP_V_MASK_SIZE; i++) - __set_bit(i, srpt_tgt->comp_v_mask); -} - /** * srpt_add_one() - Infiniband device addition callback function. */ @@ -4272,14 +4070,10 @@ static void srpt_add_one(struct ib_device *device) struct ib_cm_id *cm_id; struct srpt_device *sdev; struct srpt_port *sport; - struct srpt_tgt *srpt_tgt; struct ib_srq_init_attr srq_attr; - char tgt_name[24]; - int i, ret; + int i, j, ret; - TRACE_ENTRY(); - - TRACE_DBG("device = %p, device->dma_ops = %p", device, device->dma_ops); + pr_debug("device = %p, device->dma_ops = %p\n", device, device->dma_ops); sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); if (!sdev) @@ -4287,47 +4081,21 @@ static void srpt_add_one(struct ib_device *device) sdev->device = device; - if (!one_target_per_port) { - srpt_tgt = &sdev->srpt_tgt; - srpt_init_tgt(srpt_tgt); - - if (use_node_guid_in_target_name) { - snprintf(tgt_name, sizeof(tgt_name), - "%04x:%04x:%04x:%04x", - be16_to_cpu(((__be16 *)&device->node_guid)[0]), - be16_to_cpu(((__be16 *)&device->node_guid)[1]), - be16_to_cpu(((__be16 *)&device->node_guid)[2]), - be16_to_cpu(((__be16 *)&device->node_guid)[3])); - srpt_tgt->scst_tgt = - scst_register_target(&srpt_template, tgt_name); - } else { - srpt_tgt->scst_tgt = - scst_register_target(&srpt_template, NULL); - } - if (!srpt_tgt->scst_tgt) { - PRINT_ERROR("SCST registration failed for %s.", - sdev->device->name); - goto free_dev; - } - - scst_tgt_set_tgt_priv(srpt_tgt->scst_tgt, sdev); - } - ret = ib_query_device(device, &sdev->dev_attr); if (ret) { - PRINT_ERROR("ib_query_device() failed: %d", ret); - goto unregister_tgt; + pr_err("ib_query_device() failed: %d\n", ret); + goto free_dev; } sdev->pd = ib_alloc_pd(device); if (IS_ERR(sdev->pd)) { - PRINT_ERROR("ib_alloc_pd() failed: %ld", PTR_ERR(sdev->pd)); - goto unregister_tgt; + pr_err("ib_alloc_pd() failed: %ld\n", PTR_ERR(sdev->pd)); + goto free_dev; } sdev->mr = ib_get_dma_mr(sdev->pd, IB_ACCESS_LOCAL_WRITE); if (IS_ERR(sdev->mr)) { - PRINT_ERROR("ib_get_dma_mr() failed: %ld", PTR_ERR(sdev->mr)); + pr_err("ib_get_dma_mr() failed: %ld\n", PTR_ERR(sdev->mr)); goto err_pd; } @@ -4347,15 +4115,14 @@ static void srpt_add_one(struct ib_device *device) sdev->srq = use_srq ? ib_create_srq(sdev->pd, &srq_attr) : ERR_PTR(-ENOSYS); if (IS_ERR(sdev->srq)) { - TRACE_DBG("%s: ib_create_srq() failed: %ld", __func__, - PTR_ERR(sdev->srq)); + pr_debug("ib_create_srq() failed: %ld\n", PTR_ERR(sdev->srq)); /* SRQ not supported. */ sdev->use_srq = false; } else { - TRACE_DBG("%s: create SRQ #wr= %d max_allow=%d dev= %s", - __func__, sdev->srq_size, sdev->dev_attr.max_srq_wr, - device->name); + pr_debug("create SRQ #wr= %d max_allow=%d dev= %s\n", + sdev->srq_size, sdev->dev_attr.max_srq_wr, + device->name); sdev->use_srq = true; @@ -4366,7 +4133,7 @@ static void srpt_add_one(struct ib_device *device) DATA_ALIGNMENT_OFFSET, DMA_FROM_DEVICE); if (!sdev->ioctx_ring) { - PRINT_ERROR("srpt_alloc_ioctx_ring() failed"); + pr_err("srpt_alloc_ioctx_ring() failed\n"); goto err_mr; } @@ -4382,15 +4149,14 @@ static void srpt_add_one(struct ib_device *device) cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev); if (IS_ERR(cm_id)) { - PRINT_ERROR("ib_create_cm_id() failed: %ld", PTR_ERR(cm_id)); + pr_err("ib_create_cm_id() failed: %ld\n", PTR_ERR(cm_id)); goto err_ring; } sdev->cm_id = cm_id; /* print out target login information */ - TRACE_DBG("Target login info: id_ext=%016llx," - "ioc_guid=%016llx,pkey=ffff,service_id=%016llx", - srpt_service_guid, srpt_service_guid, srpt_service_guid); + pr_debug("Target login info: id_ext=%016llx,ioc_guid=%016llx,pkey=ffff,service_id=%016llx\n", + srpt_service_guid, srpt_service_guid, srpt_service_guid); /* * We do not have a consistent service_id (ie. also id_ext of target_id) @@ -4401,8 +4167,8 @@ static void srpt_add_one(struct ib_device *device) ret = ib_cm_listen(sdev->cm_id, cpu_to_be64(srpt_service_guid), 0, NULL); if (ret) { - PRINT_ERROR("ib_cm_listen() failed: %d (cm_id state = %d)", - ret, sdev->cm_id->state); + pr_err("ib_cm_listen() failed: %d (cm_id state = %d)\n", ret, + sdev->cm_id->state); goto err_cm; } @@ -4410,7 +4176,7 @@ static void srpt_add_one(struct ib_device *device) srpt_event_handler); ret = ib_register_event_handler(&sdev->event_handler); if (ret) { - PRINT_ERROR("ib_register_event_handler() failed: %d", ret); + pr_err("ib_register_event_handler() failed: %d\n", ret); goto err_cm; } @@ -4420,8 +4186,14 @@ static void srpt_add_one(struct ib_device *device) sport = &sdev->port[i - 1]; sport->sdev = sdev; sport->port = i; - if (one_target_per_port) - srpt_init_tgt(&sport->srpt_tgt); + INIT_LIST_HEAD(&sport->nexus_list); + init_waitqueue_head(&sport->ch_releaseQ); + mutex_init(&sport->mutex); + for (j = 0; j < sdev->device->num_comp_vectors; j++) { + if (WARN_ON_ONCE(j >= COMP_V_MASK_SIZE)) + break; + __set_bit(j, sport->comp_v_mask); + } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) && !defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19) /* * A vanilla 2.6.19 or older kernel without backported OFED @@ -4432,8 +4204,8 @@ static void srpt_add_one(struct ib_device *device) INIT_WORK(&sport->work, srpt_refresh_port_work); #endif if (srpt_refresh_port(sport)) { - PRINT_ERROR("MAD registration failed for %s-%d.", - sdev->device->name, i); + pr_err("MAD registration failed for %s-%d.\n", + sdev->device->name, i); goto err_event; } } @@ -4442,7 +4214,6 @@ static void srpt_add_one(struct ib_device *device) out: ib_set_client_data(device, &srpt_client, sdev); - TRACE_EXIT(); return; err_event: @@ -4459,14 +4230,11 @@ err_mr: ib_dereg_mr(sdev->mr); err_pd: ib_dealloc_pd(sdev->pd); -unregister_tgt: - if (!one_target_per_port) - scst_unregister_target(sdev->srpt_tgt.scst_tgt); free_dev: kfree(sdev); err: sdev = NULL; - PRINT_INFO("%s(%s) failed.", __func__, device->name); + pr_info("%s(%s) failed.\n", __func__, device->name); goto out; } @@ -4475,14 +4243,12 @@ err: */ static void srpt_remove_one(struct ib_device *device) { - int i; struct srpt_device *sdev; - - TRACE_ENTRY(); + int i; sdev = ib_get_client_data(device, &srpt_client); if (!sdev) { - PRINT_INFO("%s(%s): nothing to do.", __func__, device->name); + pr_info("%s(%s): nothing to do.\n", __func__, device->name); return; } @@ -4513,18 +4279,13 @@ static void srpt_remove_one(struct ib_device *device) * SRP_LOGIN_REQ information units can arrive while unregistering the * SCST target. */ - if (one_target_per_port) { - for (i = 0; i < sdev->device->phys_port_cnt; i++) { - struct srpt_tgt *tgt = &sdev->port[i].srpt_tgt; + for (i = 0; i < sdev->device->phys_port_cnt; i++) { + struct srpt_port *sport = &sdev->port[i]; - if (tgt->scst_tgt) { - scst_unregister_target(tgt->scst_tgt); - tgt->scst_tgt = NULL; - } + if (sport->scst_tgt) { + scst_unregister_target(sport->scst_tgt); + sport->scst_tgt = NULL; } - } else { - scst_unregister_target(sdev->srpt_tgt.scst_tgt); - sdev->srpt_tgt.scst_tgt = NULL; } srpt_free_ioctx_ring((struct srpt_ioctx **)sdev->ioctx_ring, sdev, @@ -4537,8 +4298,6 @@ static void srpt_remove_one(struct ib_device *device) ib_dealloc_pd(sdev->pd); kfree(sdev); - - TRACE_EXIT(); } static struct ib_client srpt_client = { @@ -4602,9 +4361,9 @@ static void srpt_unregister_procfs_entry(struct scst_tgt_template *tgt) * srpt_init_module() - Kernel module initialization. * * Note: Since ib_register_client() registers callback functions, and since at - * least one of these callback functions (srpt_add_one()) calls SCST functions, - * the SCST target template must be registered before ib_register_client() is - * called. + * least one of these callback functions (srpt_add_one()) calls target core + * functions, this driver must be registered with the SCSI target core before + * ib_register_client() is called. */ static int __init srpt_init_module(void) { @@ -4614,56 +4373,40 @@ static int __init srpt_init_module(void) ret = -EINVAL; if (srp_max_req_size < MIN_MAX_REQ_SIZE) { - PRINT_ERROR("invalid value %d for kernel module parameter" - " srp_max_req_size -- must be at least %d.", - srp_max_req_size, - MIN_MAX_REQ_SIZE); + pr_err("invalid value %d for kernel module parameter srp_max_req_size -- must be at least %d.\n", + srp_max_req_size, MIN_MAX_REQ_SIZE); goto out; } if (srp_max_rsp_size < MIN_MAX_RSP_SIZE) { - PRINT_ERROR("invalid value %d for kernel module parameter" - " srp_max_rsp_size -- must be at least %d.", - srp_max_rsp_size, - MIN_MAX_RSP_SIZE); + pr_err("invalid value %d for kernel module parameter srp_max_rsp_size -- must be at least %d.\n", + srp_max_rsp_size, MIN_MAX_RSP_SIZE); goto out; } if (srpt_srq_size < MIN_SRPT_SRQ_SIZE || srpt_srq_size > MAX_SRPT_SRQ_SIZE) { - PRINT_ERROR("invalid value %d for kernel module parameter" - " srpt_srq_size -- must be in the range [%d..%d].", - srpt_srq_size, MIN_SRPT_SRQ_SIZE, - MAX_SRPT_SRQ_SIZE); + pr_err("invalid value %d for kernel module parameter srpt_srq_size -- must be in the range [%d..%d].\n", + srpt_srq_size, MIN_SRPT_SRQ_SIZE, MAX_SRPT_SRQ_SIZE); goto out; } if (srpt_sq_size < MIN_SRPT_SQ_SIZE) { - PRINT_ERROR("invalid value %d for kernel module parameter" - " srpt_sq_size -- must be at least %d.", - srpt_srq_size, MIN_SRPT_SQ_SIZE); + pr_err("invalid value %d for kernel module parameter srpt_sq_size -- must be at least %d.\n", + srpt_srq_size, MIN_SRPT_SQ_SIZE); goto out; } - if (!one_target_per_port) - PRINT_WARNING("%s%s", !use_node_guid_in_target_name ? - "Using one target per HCA " : - "Using autogenerated target names ", - "is deprecated and will be removed in one of the " - "next versions. It is strongly recommended to " - "set the one_target_per_port parameter to true " - "and to update your SCST config file."); - ret = scst_register_target_template(&srpt_template); if (ret < 0) { - PRINT_ERROR("couldn't register with scst"); + pr_err("couldn't register target template\n"); ret = -ENODEV; goto out; } ret = ib_register_client(&srpt_client); if (ret) { - PRINT_ERROR("couldn't register IB client"); + pr_err("couldn't register IB client\n"); goto out_unregister_target; } @@ -4680,7 +4423,7 @@ static int __init srpt_init_module(void) #endif if (IS_ERR(rdma_cm_id)) { rdma_cm_id = NULL; - PRINT_ERROR("RDMA/CM ID creation failed"); + pr_err("RDMA/CM ID creation failed\n"); goto out_unregister_client; } @@ -4689,13 +4432,13 @@ static int __init srpt_init_module(void) addr.sin_family = AF_INET; addr.sin_port = cpu_to_be16(rdma_cm_port); if (rdma_bind_addr(rdma_cm_id, (void *)&addr)) { - PRINT_ERROR("Binding RDMA/CM ID to port %u failed\n", - rdma_cm_port); + pr_err("Binding RDMA/CM ID to port %u failed\n", + rdma_cm_port); goto out_unregister_client; } if (rdma_listen(rdma_cm_id, 128)) { - PRINT_ERROR("rdma_listen() failed"); + pr_err("rdma_listen() failed\n"); goto out_unregister_client; } } @@ -4703,7 +4446,7 @@ static int __init srpt_init_module(void) #ifdef CONFIG_SCST_PROC ret = srpt_register_procfs_entry(&srpt_template); if (ret) { - PRINT_ERROR("couldn't register procfs entry"); + pr_err("couldn't register procfs entry\n"); goto out_rdma_cm; } #endif /* CONFIG_SCST_PROC */ @@ -4724,8 +4467,6 @@ out: static void __exit srpt_cleanup_module(void) { - TRACE_ENTRY(); - if (rdma_cm_id) rdma_destroy_id(rdma_cm_id); ib_unregister_client(&srpt_client); @@ -4733,8 +4474,6 @@ static void __exit srpt_cleanup_module(void) srpt_unregister_procfs_entry(&srpt_template); #endif /* CONFIG_SCST_PROC */ scst_unregister_target_template(&srpt_template); - - TRACE_EXIT(); } module_init(srpt_init_module); diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 7d78a7d47..5e87a4141 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2006 - 2009 Mellanox Technology Inc. All rights reserved. - * Copyright (C) 2009 - 2014 Bart Van Assche . + * Copyright (C) 2009 - 2015 Bart Van Assche . * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -66,7 +66,6 @@ #define SRP_SERVICE_NAME_PREFIX "SRP.T10:" struct srpt_nexus; -struct srpt_tgt; enum { /* @@ -254,8 +253,6 @@ struct srpt_tsk_mgmt { * @state: I/O context state. * @rdma_aborted: If initiating a multipart RDMA transfer failed, whether * the already initiated transfers have finished. - * @scmnd: SCST command data structure. - * @dir: Data direction. * @free_list: Node in srpt_rdma_ch.free_list. * @sg_cnt: SG-list size. * @mapped_sg_count: ib_dma_map_sg() return value. @@ -266,6 +263,8 @@ struct srpt_tsk_mgmt { * SRP response sent. * @tsk_mgmt: SRPT task management function context information. * @rdma_ius_buf: Inline rdma_ius buffer for small requests. + * @cmd: SCST command data structure. + * @dir: Data direction. */ struct srpt_send_ioctx { struct srpt_ioctx ioctx; @@ -281,8 +280,6 @@ struct srpt_send_ioctx { spinlock_t spinlock; enum srpt_command_state state; bool rdma_aborted; - struct scst_cmd scmnd; - scst_data_direction dir; int sg_cnt; int mapped_sg_count; u16 n_rdma_ius; @@ -293,6 +290,8 @@ struct srpt_send_ioctx { u8 rdma_ius_buf[2 * sizeof(struct rdma_iu) + 2 * sizeof(struct ib_sge)] __aligned(sizeof(uint64_t)); + struct scst_cmd cmd; + scst_data_direction dir; }; /** @@ -328,7 +327,6 @@ enum rdma_ch_state { * @sq_wr_avail: number of work requests available in the send queue. * @sport: pointer to the information of the HCA port used by this * channel. - * @srpt_tgt: Target port used by this channel. * @max_ti_iu_len: maximum target-to-initiator information unit length. * @req_lim: request limit: maximum number of requests that may be sent * by the initiator without having received a response. @@ -338,6 +336,7 @@ enum rdma_ch_state { * @spinlock: Protects free_list. * @free_list: Head of list with free send I/O contexts. * @ioctx_ring: Send I/O context ring. + * @ioctx_recv_ring: Receive I/O context ring. * @wc: Work completion array. * @state: channel state. See also enum rdma_ch_state. * @processing_wait_list: Whether or not cmd_wait_list is being processed. @@ -347,8 +346,10 @@ enum rdma_ch_state { * against concurrent modification by the cm_id spinlock. * @pkey: P_Key of the IB partition for this SRP channel. * @comp_vector: Completion vector assigned to the QP. - * @scst_sess: SCST session information associated with this SRP channel. + * @using_rdma_cm: Whether to use the RDMA/CM or the IB/CM. + * @processing_wait_list: Whether the I/O context wait list is being processed. * @sess_name: SCST session name. + * @sess: SCST session information associated with this SRP channel. */ struct srpt_rdma_ch { struct task_struct *thread; @@ -369,7 +370,6 @@ struct srpt_rdma_ch { int max_rsp_size; atomic_t sq_wr_avail; struct srpt_port *sport; - struct srpt_tgt *srpt_tgt; int max_ti_iu_len; int req_lim; int req_lim_delta; @@ -387,15 +387,14 @@ struct srpt_rdma_ch { #endif bool using_rdma_cm; bool processing_wait_list; - - struct scst_session *scst_sess; u8 sess_name[40]; + struct scst_session *sess; }; /** * struct srpt_nexus - I_T nexus - * @entry: srpt_tgt.nexus_list list node. - * @ch_list: struct srpt_rdma_ch list. Protected by srpt_tgt.mutex. + * @entry: srpt_port.nexus_list list node. + * @ch_list: struct srpt_rdma_ch list. Protected by srpt_port.mutex. * @i_port_id: 128-bit initiator port identifier copied from SRP_LOGIN_REQ. * @t_port_id: 128-bit target port identifier copied from SRP_LOGIN_REQ. */ @@ -406,26 +405,6 @@ struct srpt_nexus { u8 t_port_id[16]; }; -/** - * struct srpt_tgt - * @ch_releaseQ: Enables waiting for removal from nexus_list. - * @mutex: Protects @nexus_list and srpt_nexus.ch_list. - * @nexus_list: Per-device I_T nexus list. - * @scst_tgt: SCST target information associated with this HCA. - * @comp_v_mask: Bitmask with one bit per allowed completion vector. - * @comp_vector: Completion vector from where searching will start. - * @enabled: Whether or not this SCST target is enabled. - */ -struct srpt_tgt { - wait_queue_head_t ch_releaseQ; - struct mutex mutex; - struct list_head nexus_list; - struct scst_tgt *scst_tgt; - DECLARE_BITMAP(comp_v_mask, COMP_V_MASK_SIZE); - u8 comp_vector; - bool enabled; -}; - /** * struct srpt_port - Information associated by SRPT with a single IB port. * @sdev: backpointer to the HCA information. @@ -435,8 +414,13 @@ struct srpt_tgt { * @lid: cached value of the port's lid. * @gid: cached value of the port's gid. * @work: work structure for refreshing the aforementioned cached values. - * @srpt_tgt: Target port information. Only used if one-target-per-port - * mode is enabled. + * @ch_releaseQ: Enables waiting for removal from nexus_list. + * @mutex: Protects @nexus_list and srpt_nexus.ch_list. + * @nexus_list: Per-device I_T nexus list. + * @scst_tgt: SCST target information associated with this HCA. + * @comp_v_mask: Bitmask with one bit per allowed completion vector. + * @comp_vector: Completion vector from where searching will start. + * @enabled: Whether or not this SCST target is enabled. */ struct srpt_port { struct srpt_device *sdev; @@ -446,7 +430,13 @@ struct srpt_port { u16 lid; union ib_gid gid; struct work_struct work; - struct srpt_tgt srpt_tgt; + wait_queue_head_t ch_releaseQ; + struct mutex mutex; + struct list_head nexus_list; + struct scst_tgt *scst_tgt; + DECLARE_BITMAP(comp_v_mask, COMP_V_MASK_SIZE); + u8 comp_vector; + bool enabled; }; /** @@ -463,8 +453,6 @@ struct srpt_port { * @ioctx_ring: Per-HCA SRQ. * @port: Information about the ports owned by this HCA. * @event_handler: Per-HCA asynchronous IB event handler. - * @srpt_tgt: Target port information. Only used if one-target-per-port - * mode is disabled. */ struct srpt_device { struct ib_device *device; @@ -478,7 +466,6 @@ struct srpt_device { struct srpt_recv_ioctx **ioctx_ring; struct srpt_port port[2]; struct ib_event_handler event_handler; - struct srpt_tgt srpt_tgt; }; /** diff --git a/usr/fileio/Makefile b/usr/fileio/Makefile index 6c8f68991..8dfd4ad6d 100644 --- a/usr/fileio/Makefile +++ b/usr/fileio/Makefile @@ -1,8 +1,8 @@ # # SCSI target mid-level makefile # -# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2014 Fusion-io, Inc. +# Copyright (C) 2007 - 2015 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2015 SanDisk Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/usr/fileio/common.c b/usr/fileio/common.c index 75d09a7d7..feb4e8b3c 100644 --- a/usr/fileio/common.c +++ b/usr/fileio/common.c @@ -1,8 +1,8 @@ /* * common.c * - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/usr/fileio/common.h b/usr/fileio/common.h index 957377795..e36697332 100644 --- a/usr/fileio/common.h +++ b/usr/fileio/common.h @@ -1,8 +1,8 @@ /* * common.h * - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/usr/fileio/debug.c b/usr/fileio/debug.c index ea1d9eb85..8e93a70e1 100644 --- a/usr/fileio/debug.c +++ b/usr/fileio/debug.c @@ -1,9 +1,9 @@ /* * debug.c * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Contains helper functions for execution tracing and error reporting. * Intended to be included in main .c file. diff --git a/usr/fileio/debug.h b/usr/fileio/debug.h index fc698e8f6..dbd0b8af1 100644 --- a/usr/fileio/debug.h +++ b/usr/fileio/debug.h @@ -1,9 +1,9 @@ /* * debug.h * - * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 SanDisk Corporation * * Contains macroses for execution tracing and error reporting * diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index cfb5d21b6..935f75d70 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -1,8 +1,8 @@ /* * fileio.c * - * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2007 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2015 SanDisk Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -476,6 +476,7 @@ out_unreg: alarm(0); for (i = 0; i < num_devs; i++) { if (unreg_before_close) { + /* Just to see the obsolete call message */ res = ioctl(devs[i].scst_usr_fd, SCST_USER_UNREGISTER_DEVICE, NULL); if (res != 0) { res = errno; diff --git a/www/comparison.html b/www/comparison.html index 5f00293ea..f3ca1e4ea 100644 --- a/www/comparison.html +++ b/www/comparison.html @@ -547,7 +547,7 @@ target reconfiguration in a PnP-like manner) + -

diff --git a/www/contributing.html b/www/contributing.html index 3c356390e..0d049f783 100644 --- a/www/contributing.html +++ b/www/contributing.html @@ -204,7 +204,7 @@ diff --git a/www/downloads.html b/www/downloads.html index e7ce54f76..d1bad5235 100644 --- a/www/downloads.html +++ b/www/downloads.html @@ -36,7 +36,7 @@

SCST Downloads

-

The latest stable version of SCST is 3.0.0. The latest updates for it +

The latest stable version of SCST is 3.0.1. The latest updates for it you can find it in the SVN branch 3.0.x.

You can also download prebuilt SCST modules for @@ -94,7 +94,7 @@

diff --git a/www/handler_fileio_tgt.html b/www/handler_fileio_tgt.html index 65e76ee9f..e6321f956 100644 --- a/www/handler_fileio_tgt.html +++ b/www/handler_fileio_tgt.html @@ -68,7 +68,7 @@ All the words about BLOCKIO mode from SCST's README file apply to O_DIRECT mode as well.

-

The latest stable version is 3.0.0. Requires SCST version 3.0.0 or higher.

+

The latest stable version is 3.0.1. Requires SCST version 3.0.1 or higher.

You can find the latest development version of this handler in the SCST SVN. See the download page how to setup access to it.

diff --git a/www/images/LPe16002.jpg b/www/images/LPe16002.jpg new file mode 100644 index 000000000..ce16b045b Binary files /dev/null and b/www/images/LPe16002.jpg differ diff --git a/www/index.html b/www/index.html index 4f5314802..195602770 100644 --- a/www/index.html +++ b/www/index.html @@ -186,7 +186,7 @@ diff --git a/www/mc_s.html b/www/mc_s.html index c7675a7bc..a351b4c22 100644 --- a/www/mc_s.html +++ b/www/mc_s.html @@ -247,7 +247,7 @@ and here. diff --git a/www/scst_admin.html b/www/scst_admin.html index 17903a525..4b6d3117d 100644 --- a/www/scst_admin.html +++ b/www/scst_admin.html @@ -67,7 +67,7 @@ scst.conf, then you edit this file, e.g. add new devices, then scstadmin will figure out that you added those devices and add them to SCST.

-

The latest stable version is 3.0.0.

+

The latest stable version is 3.0.1.

diff --git a/www/scstvslio.html b/www/scstvslio.html index 06185548b..eb9d322de 100644 --- a/www/scstvslio.html +++ b/www/scstvslio.html @@ -100,7 +100,7 @@ diff --git a/www/scstvsstgt.html b/www/scstvsstgt.html index dba073301..132312d5f 100644 --- a/www/scstvsstgt.html +++ b/www/scstvsstgt.html @@ -80,7 +80,7 @@ diff --git a/www/solutions.html b/www/solutions.html index 22a0456dc..4141e3ca2 100644 --- a/www/solutions.html +++ b/www/solutions.html @@ -57,7 +57,7 @@ diff --git a/www/target_emulex.html b/www/target_emulex.html index 9d5668bca..ca9210d5e 100644 --- a/www/target_emulex.html +++ b/www/target_emulex.html @@ -57,29 +57,21 @@

Target driver for Emulex FC/FCoE adapters

-

SCST Emulex +

SCST Emulex -

The ocs_fc_scst target driver for Emulex FC/FCoE adapters is developed by the Emulex team - and is available at the Emulex OneCore SDK - developer site. Register for free at the Developer Portal Access - page to get the source code and documentation. The Emulex OneCore SDK is built around the Emulex SLI-4 - (Service Level Interface) API and is compatible with Emulex 16 Gb/s Fibre Channel HBAs (LPe16000 series) - and Ethernet based target mode FCoE UCNAs (OCe11102-F series).

+

The Emulex OneCore Storage FC/FCoE driver (ocs_fc_scst) is developed and maintained by Emulex. It is available on the emulex.com download page under OneCore Storage Drivers. + The Emulex OneCore driver supports the Service Level Interface 4 (SLI-4) API and is compatible with the latest generation of Emulex 8 and 16 Gb/s Fibre Channel HBAs (LPe15000 and LPe16000 series), + as well as the latest generation of 10 and 40 Gb/s FCoE UCNAs (OCe14000-series).

-

The ocs_fc_scst driver allows for Emulex adapters to be placed in initiator and/or target mode. - The driver effectively maps SCST to the Emulex SLI-4 interface allowing a simple transition to Emulex 16Gb/s - fibre channel technology for existing or new SCST users. NPIV is also supported, allowing virtual - ports to be created with individual SCST target instances bound to them. +

The ocs_fc_scst driver has been tested with SCST version 3.0.1, as well as SCST version 2.2.0. The driver supports both target and initiator mode of operation and a number of + advanced features: NPIV, T10-PI, etc. Please refer to the Emulex download page for more information. -

Documented for use with RHEL/Centos 6.x based distributions, ocs_fc_scst works with the stable SCST 2.2.1 - as well as the development versions of 2.2.x and 3.0.x.

- -

Please note that the drivers on SourceForge Emulex Drivers - are now very old, not maintained and so are not recommended for new designs.

+

Note: The drivers on SourceForge Emulex Drivers + are now very old, not maintained and not recommended for new designs.



 
@@ -90,7 +82,7 @@ diff --git a/www/target_fcoe.html b/www/target_fcoe.html index 7dac1a4a5..51c2afd47 100644 --- a/www/target_fcoe.html +++ b/www/target_fcoe.html @@ -61,7 +61,7 @@ SCST Fibre Channel over Ethernet (FCoE) target is developed by Open-FCoE team and Joe Eykholt. Since February 2010 the main development place of it is SCST SVN repository.

-

The latest stable version is 3.0.0. You can download the latest development version from the SCST SVN repository. See the download +

The latest stable version is 3.0.1. You can download the latest development version from the SCST SVN repository. See the download page how to setup access to it.





diff --git a/www/target_ibmvscsi.html b/www/target_ibmvscsi.html index 6b01c9621..f9abc7446 100644 --- a/www/target_ibmvscsi.html +++ b/www/target_ibmvscsi.html @@ -78,7 +78,7 @@ diff --git a/www/target_iscsi.html b/www/target_iscsi.html index b642efe4f..bf58b0183 100644 --- a/www/target_iscsi.html +++ b/www/target_iscsi.html @@ -113,7 +113,7 @@ the SCST core. You can also use a migration tool developed by Scalable Informatics Inc., which will convert your IET machine to an iSCSI-SCST machine. See README for more details.

-

The latest stable version is 3.0.0. Requires Linux kernel version 2.6.18.x or higher and SCST version 3.0.0 or higher. +

The latest stable version is 3.0.1. Requires Linux kernel version 2.6.18.x or higher and SCST version 3.0.1 or higher. Tested mostly on i386 and x86_64, but should work on any other supported by Linux platform.

You can find the latest development version of this driver in the SCST SVN. See the download page how to setup access to it.

@@ -138,7 +138,7 @@ diff --git a/www/target_iser.html b/www/target_iser.html index fd29367e8..67bf344a9 100644 --- a/www/target_iser.html +++ b/www/target_iser.html @@ -59,11 +59,11 @@

iSCSI Extensions for RDMA (iSER) driver for iSCSI-SCST

ISER extension for ISCSI-SCST has been developed by Yan Burman and Mellanox Technologies (thank you!).

-

Current version is 3.0.0. You can find the latest development version in the SCST SVN in iser branch. See the download page how to setup +

Current version is 3.0.1. You can find the latest development version in the SCST SVN in iser branch. See the download page how to setup access to it.

3.0.x-iser branch in the SCST SVN is the stable post-3.0 release iSER - branch with the latest stable fixes in the iSER driver as well as other SCST components merged from the 3.0.0 branch.

+ branch with the latest stable fixes in the iSER driver as well as other SCST components merged from the 3.0.x branch.




diff --git a/www/target_local.html b/www/target_local.html index 55efbc50c..570c8158e 100644 --- a/www/target_local.html +++ b/www/target_local.html @@ -70,7 +70,7 @@

This driver was made by Richard Sharpe.

-

It is on the beta stage. The latest stabe version is 3.0.0. You can download +

It is on the beta stage. The latest stabe version is 3.0.1. You can download the latest development version from the SCST SVN repository. See the download page how to setup access to it.




diff --git a/www/target_lsi.html b/www/target_lsi.html index 1f8ab013b..b96496b0b 100644 --- a/www/target_lsi.html +++ b/www/target_lsi.html @@ -75,7 +75,7 @@ diff --git a/www/target_mvsas.html b/www/target_mvsas.html index 4941e8751..2e3984aaa 100644 --- a/www/target_mvsas.html +++ b/www/target_mvsas.html @@ -75,7 +75,7 @@ diff --git a/www/target_old.html b/www/target_old.html index 6de51239b..bae199446 100644 --- a/www/target_old.html +++ b/www/target_old.html @@ -111,7 +111,7 @@ diff --git a/www/target_qla2x00t.html b/www/target_qla2x00t.html index cfb27f38a..493a55b29 100644 --- a/www/target_qla2x00t.html +++ b/www/target_qla2x00t.html @@ -55,23 +55,35 @@ -
+

Target driver qla2x00t for QLogic FC adapters

SCST QLogic This is target driver for QLogic qla2xxx (22xx++) Fibre Channel adapters.

-

The latest stable version is 3.0.0. Requires Linux kernel version 2.6.26.x or higher and - SCST version 3.0.0 or higher.

- -

Driver in qla2x00t subdirectory is the old one, forked from qla2xxx from kernel 2.6.26. It is not maintained anymore. - You can find the latest version of this driver in - git://git.qlogic.com/scst-qla2xxx.git. It is now maintained by QLogic, hence - located in the QLogic's git. This driver also supports FCoE. See SVN root README for instructions how to integrate it - into the SCST build tree.

+

There are 2 latest stable versions of this driver:

+
    +
  • The first one is 3.0.1. It requires Linux kernel version 2.6.26.x or higher and + SCST version 3.0.1 or higher. Driver in qla2x00t subdirectory is the old one, forked from + qla2xxx from kernel 2.6.26. It does not support 16G QLogic adapters and is not maintained anymore.
  • +
  • Another version you can find in the SVN trunk. This driver supports 16G Hilda QLogic + chip based adapters (post-Hilda QLogic chips not supported) and has many other important improvements. + This driver should also support FCoE, but that has never been verified. + It has passed intensive internal SanDisk tests. It is stable and production ready. + This driver is in stable maintenance mode in favor of the + QLogic git tree driver (see below). This is the recommended driver to use in production at the moment.
  • +
+

+ The latest version of this driver with full support of the latest QLogic chips for both FC and FCoE you can find in + git://git.qlogic.com/scst-qla2xxx.git. This driver also supports T10-PI functionality. + It is maintained by QLogic, hence located in the QLogic's git. See SVN root README + for instructions how to integrate it into the SCST build tree. However, not all + improvements from the SVN trunk target driver have been integrated in this driver yet, + so it is not as stable and functional as the SVN trunk driver.

 
@@ -82,7 +94,7 @@
diff --git a/www/target_srp.html b/www/target_srp.html index afc613f51..ed6e8e4e8 100644 --- a/www/target_srp.html +++ b/www/target_srp.html @@ -63,7 +63,7 @@ It is maintained by Bart Van Assche.

This driver is mainline Linux kernel ready and going to be pushed to it together with other SCST patches.

-

The latest stable version is 3.0.0.

+

The latest stable version is 3.0.1.




diff --git a/www/targets.html b/www/targets.html index 892d9afdc..412ae3c4f 100644 --- a/www/targets.html +++ b/www/targets.html @@ -78,7 +78,7 @@ diff --git a/www/users.html b/www/users.html index 7283da367..a9a94d58b 100644 --- a/www/users.html +++ b/www/users.html @@ -167,7 +167,7 @@