| /* |
| * QLogic iSCSI HBA Driver |
| * Copyright (c) 2003-2013 QLogic Corporation |
| * |
| * See LICENSE.qla4xxx for copyright and licensing details. |
| */ |
| #include <linux/moduleparam.h> |
| #include <linux/slab.h> |
| #include <linux/blkdev.h> |
| #include <linux/iscsi_boot_sysfs.h> |
| #include <linux/inet.h> |
| |
| #include <scsi/scsi_tcq.h> |
| #include <scsi/scsicam.h> |
| |
| #include "ql4_def.h" |
| #include "ql4_version.h" |
| #include "ql4_glbl.h" |
| #include "ql4_dbg.h" |
| #include "ql4_inline.h" |
| #include "ql4_83xx.h" |
| |
| /* |
| * Driver version |
| */ |
| static char qla4xxx_version_str[40]; |
| |
| /* |
| * SRB allocation cache |
| */ |
| static struct kmem_cache *srb_cachep; |
| |
| /* |
| * Module parameter information and variables |
| */ |
| static int ql4xdisablesysfsboot = 1; |
| module_param(ql4xdisablesysfsboot, int, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(ql4xdisablesysfsboot, |
| " Set to disable exporting boot targets to sysfs.\n" |
| "\t\t 0 - Export boot targets\n" |
| "\t\t 1 - Do not export boot targets (Default)"); |
| |
| int ql4xdontresethba; |
| module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(ql4xdontresethba, |
| " Don't reset the HBA for driver recovery.\n" |
| "\t\t 0 - It will reset HBA (Default)\n" |
| "\t\t 1 - It will NOT reset HBA"); |
| |
| int ql4xextended_error_logging; |
| module_param(ql4xextended_error_logging, int, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(ql4xextended_error_logging, |
| " Option to enable extended error logging.\n" |
| "\t\t 0 - no logging (Default)\n" |
| "\t\t 2 - debug logging"); |
| |
| int ql4xenablemsix = 1; |
| module_param(ql4xenablemsix, int, S_IRUGO|S_IWUSR); |
| MODULE_PARM_DESC(ql4xenablemsix, |
| " Set to enable MSI or MSI-X interrupt mechanism.\n" |
| "\t\t 0 = enable INTx interrupt mechanism.\n" |
| "\t\t 1 = enable MSI-X interrupt mechanism (Default).\n" |
| "\t\t 2 = enable MSI interrupt mechanism."); |
| |
| #define QL4_DEF_QDEPTH 32 |
| static int ql4xmaxqdepth = QL4_DEF_QDEPTH; |
| module_param(ql4xmaxqdepth, int, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(ql4xmaxqdepth, |
| " Maximum queue depth to report for target devices.\n" |
| "\t\t Default: 32."); |
| |
| static int ql4xqfulltracking = 1; |
| module_param(ql4xqfulltracking, int, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(ql4xqfulltracking, |
| " Enable or disable dynamic tracking and adjustment of\n" |
| "\t\t scsi device queue depth.\n" |
| "\t\t 0 - Disable.\n" |
| "\t\t 1 - Enable. (Default)"); |
| |
| static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO; |
| module_param(ql4xsess_recovery_tmo, int, S_IRUGO); |
| MODULE_PARM_DESC(ql4xsess_recovery_tmo, |
| " Target Session Recovery Timeout.\n" |
| "\t\t Default: 120 sec."); |
| |
| int ql4xmdcapmask = 0; |
| module_param(ql4xmdcapmask, int, S_IRUGO); |
| MODULE_PARM_DESC(ql4xmdcapmask, |
| " Set the Minidump driver capture mask level.\n" |
| "\t\t Default is 0 (firmware default capture mask)\n" |
| "\t\t Can be set to 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF"); |
| |
| int ql4xenablemd = 1; |
| module_param(ql4xenablemd, int, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(ql4xenablemd, |
| " Set to enable minidump.\n" |
| "\t\t 0 - disable minidump\n" |
| "\t\t 1 - enable minidump (Default)"); |
| |
| static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha); |
| /* |
| * SCSI host template entry points |
| */ |
| static void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha); |
| |
| /* |
| * iSCSI template entry points |
| */ |
| static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess, |
| enum iscsi_param param, char *buf); |
| static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn, |
| enum iscsi_param param, char *buf); |
| static int qla4xxx_host_get_param(struct Scsi_Host *shost, |
| enum iscsi_host_param param, char *buf); |
| static int qla4xxx_iface_set_param(struct Scsi_Host *shost, void *data, |
| uint32_t len); |
| static int qla4xxx_get_iface_param(struct iscsi_iface *iface, |
| enum iscsi_param_type param_type, |
| int param, char *buf); |
| static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc); |
| static struct iscsi_endpoint *qla4xxx_ep_connect(struct Scsi_Host *shost, |
| struct sockaddr *dst_addr, |
| int non_blocking); |
| static int qla4xxx_ep_poll(struct iscsi_endpoint *ep, int timeout_ms); |
| static void qla4xxx_ep_disconnect(struct iscsi_endpoint *ep); |
| static int qla4xxx_get_ep_param(struct iscsi_endpoint *ep, |
| enum iscsi_param param, char *buf); |
| static int qla4xxx_conn_start(struct iscsi_cls_conn *conn); |
| static struct iscsi_cls_conn * |
| qla4xxx_conn_create(struct iscsi_cls_session *cls_sess, uint32_t conn_idx); |
| static int qla4xxx_conn_bind(struct iscsi_cls_session *cls_session, |
| struct iscsi_cls_conn *cls_conn, |
| uint64_t transport_fd, int is_leading); |
| static void qla4xxx_conn_destroy(struct iscsi_cls_conn *conn); |
| static struct iscsi_cls_session * |
| qla4xxx_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, |
| uint16_t qdepth, uint32_t initial_cmdsn); |
| static void qla4xxx_session_destroy(struct iscsi_cls_session *sess); |
| static void qla4xxx_task_work(struct work_struct *wdata); |
| static int qla4xxx_alloc_pdu(struct iscsi_task *, uint8_t); |
| static int qla4xxx_task_xmit(struct iscsi_task *); |
| static void qla4xxx_task_cleanup(struct iscsi_task *); |
| static void qla4xxx_fail_session(struct iscsi_cls_session *cls_session); |
| static void qla4xxx_conn_get_stats(struct iscsi_cls_conn *cls_conn, |
| struct iscsi_stats *stats); |
| static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num, |
| uint32_t iface_type, uint32_t payload_size, |
| uint32_t pid, struct sockaddr *dst_addr); |
| static int qla4xxx_get_chap_list(struct Scsi_Host *shost, uint16_t chap_tbl_idx, |
| uint32_t *num_entries, char *buf); |
| static int qla4xxx_delete_chap(struct Scsi_Host *shost, uint16_t chap_tbl_idx); |
| static int qla4xxx_set_chap_entry(struct Scsi_Host *shost, void *data, |
| int len); |
| static int qla4xxx_get_host_stats(struct Scsi_Host *shost, char *buf, int len); |
| |
| /* |
| * SCSI host template entry points |
| */ |
| static int qla4xxx_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *cmd); |
| static int qla4xxx_eh_abort(struct scsi_cmnd *cmd); |
| static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); |
| static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); |
| static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); |
| static int qla4xxx_slave_alloc(struct scsi_device *device); |
| static umode_t qla4_attr_is_visible(int param_type, int param); |
| static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type); |
| |
| /* |
| * iSCSI Flash DDB sysfs entry points |
| */ |
| static int |
| qla4xxx_sysfs_ddb_set_param(struct iscsi_bus_flash_session *fnode_sess, |
| struct iscsi_bus_flash_conn *fnode_conn, |
| void *data, int len); |
| static int |
| qla4xxx_sysfs_ddb_get_param(struct iscsi_bus_flash_session *fnode_sess, |
| int param, char *buf); |
| static int qla4xxx_sysfs_ddb_add(struct Scsi_Host *shost, const char *buf, |
| int len); |
| static int |
| qla4xxx_sysfs_ddb_delete(struct iscsi_bus_flash_session *fnode_sess); |
| static int qla4xxx_sysfs_ddb_login(struct iscsi_bus_flash_session *fnode_sess, |
| struct iscsi_bus_flash_conn *fnode_conn); |
| static int qla4xxx_sysfs_ddb_logout(struct iscsi_bus_flash_session *fnode_sess, |
| struct iscsi_bus_flash_conn *fnode_conn); |
| static int qla4xxx_sysfs_ddb_logout_sid(struct iscsi_cls_session *cls_sess); |
| |
| static struct qla4_8xxx_legacy_intr_set legacy_intr[] = |
| QLA82XX_LEGACY_INTR_CONFIG; |
| |
| static struct scsi_host_template qla4xxx_driver_template = { |
| .module = THIS_MODULE, |
| .name = DRIVER_NAME, |
| .proc_name = DRIVER_NAME, |
| .queuecommand = qla4xxx_queuecommand, |
| |
| .eh_abort_handler = qla4xxx_eh_abort, |
| .eh_device_reset_handler = qla4xxx_eh_device_reset, |
| .eh_target_reset_handler = qla4xxx_eh_target_reset, |
| .eh_host_reset_handler = qla4xxx_eh_host_reset, |
| .eh_timed_out = qla4xxx_eh_cmd_timed_out, |
| |
| .slave_alloc = qla4xxx_slave_alloc, |
| .change_queue_depth = scsi_change_queue_depth, |
| |
| .this_id = -1, |
| .cmd_per_lun = 3, |
| .use_clustering = ENABLE_CLUSTERING, |
| .sg_tablesize = SG_ALL, |
| |
| .max_sectors = 0xFFFF, |
| .shost_attrs = qla4xxx_host_attrs, |
| .host_reset = qla4xxx_host_reset, |
| .vendor_id = SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC, |
| }; |
| |
| static struct iscsi_transport qla4xxx_iscsi_transport = { |
| .owner = THIS_MODULE, |
| .name = DRIVER_NAME, |
| .caps = CAP_TEXT_NEGO | |
| CAP_DATA_PATH_OFFLOAD | CAP_HDRDGST | |
| CAP_DATADGST | CAP_LOGIN_OFFLOAD | |
| CAP_MULTI_R2T, |
| .attr_is_visible = qla4_attr_is_visible, |
| .create_session = qla4xxx_session_create, |
| .destroy_session = qla4xxx_session_destroy, |
| .start_conn = qla4xxx_conn_start, |
| .create_conn = qla4xxx_conn_create, |
| .bind_conn = qla4xxx_conn_bind, |
| .stop_conn = iscsi_conn_stop, |
| .destroy_conn = qla4xxx_conn_destroy, |
| .set_param = iscsi_set_param, |
| .get_conn_param = qla4xxx_conn_get_param, |
| .get_session_param = qla4xxx_session_get_param, |
| .get_ep_param = qla4xxx_get_ep_param, |
| .ep_connect = qla4xxx_ep_connect, |
| .ep_poll = qla4xxx_ep_poll, |
| .ep_disconnect = qla4xxx_ep_disconnect, |
| .get_stats = qla4xxx_conn_get_stats, |
| .send_pdu = iscsi_conn_send_pdu, |
| .xmit_task = qla4xxx_task_xmit, |
| .cleanup_task = qla4xxx_task_cleanup, |
| .alloc_pdu = qla4xxx_alloc_pdu, |
| |
| .get_host_param = qla4xxx_host_get_param, |
| .set_iface_param = qla4xxx_iface_set_param, |
| .get_iface_param = qla4xxx_get_iface_param, |
| .bsg_request = qla4xxx_bsg_request, |
| .send_ping = qla4xxx_send_ping, |
| .get_chap = qla4xxx_get_chap_list, |
| .delete_chap = qla4xxx_delete_chap, |
| .set_chap = qla4xxx_set_chap_entry, |
| .get_flashnode_param = qla4xxx_sysfs_ddb_get_param, |
| .set_flashnode_param = qla4xxx_sysfs_ddb_set_param, |
| .new_flashnode = qla4xxx_sysfs_ddb_add, |
| .del_flashnode = qla4xxx_sysfs_ddb_delete, |
| .login_flashnode = qla4xxx_sysfs_ddb_login, |
| .logout_flashnode = qla4xxx_sysfs_ddb_logout, |
| .logout_flashnode_sid = qla4xxx_sysfs_ddb_logout_sid, |
| .get_host_stats = qla4xxx_get_host_stats, |
| }; |
| |
| static struct scsi_transport_template *qla4xxx_scsi_transport; |
| |
| static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num, |
| uint32_t iface_type, uint32_t payload_size, |
| uint32_t pid, struct sockaddr *dst_addr) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| struct sockaddr_in *addr; |
| struct sockaddr_in6 *addr6; |
| uint32_t options = 0; |
| uint8_t ipaddr[IPv6_ADDR_LEN]; |
| int rval; |
| |
| memset(ipaddr, 0, IPv6_ADDR_LEN); |
| /* IPv4 to IPv4 */ |
| if ((iface_type == ISCSI_IFACE_TYPE_IPV4) && |
| (dst_addr->sa_family == AF_INET)) { |
| addr = (struct sockaddr_in *)dst_addr; |
| memcpy(ipaddr, &addr->sin_addr.s_addr, IP_ADDR_LEN); |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv4 Ping src: %pI4 " |
| "dest: %pI4\n", __func__, |
| &ha->ip_config.ip_address, ipaddr)); |
| rval = qla4xxx_ping_iocb(ha, options, payload_size, pid, |
| ipaddr); |
| if (rval) |
| rval = -EINVAL; |
| } else if ((iface_type == ISCSI_IFACE_TYPE_IPV6) && |
| (dst_addr->sa_family == AF_INET6)) { |
| /* IPv6 to IPv6 */ |
| addr6 = (struct sockaddr_in6 *)dst_addr; |
| memcpy(ipaddr, &addr6->sin6_addr.in6_u.u6_addr8, IPv6_ADDR_LEN); |
| |
| options |= PING_IPV6_PROTOCOL_ENABLE; |
| |
| /* Ping using LinkLocal address */ |
| if ((iface_num == 0) || (iface_num == 1)) { |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: LinkLocal Ping " |
| "src: %pI6 dest: %pI6\n", __func__, |
| &ha->ip_config.ipv6_link_local_addr, |
| ipaddr)); |
| options |= PING_IPV6_LINKLOCAL_ADDR; |
| rval = qla4xxx_ping_iocb(ha, options, payload_size, |
| pid, ipaddr); |
| } else { |
| ql4_printk(KERN_WARNING, ha, "%s: iface num = %d " |
| "not supported\n", __func__, iface_num); |
| rval = -ENOSYS; |
| goto exit_send_ping; |
| } |
| |
| /* |
| * If ping using LinkLocal address fails, try ping using |
| * IPv6 address |
| */ |
| if (rval != QLA_SUCCESS) { |
| options &= ~PING_IPV6_LINKLOCAL_ADDR; |
| if (iface_num == 0) { |
| options |= PING_IPV6_ADDR0; |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv6 " |
| "Ping src: %pI6 " |
| "dest: %pI6\n", __func__, |
| &ha->ip_config.ipv6_addr0, |
| ipaddr)); |
| } else if (iface_num == 1) { |
| options |= PING_IPV6_ADDR1; |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv6 " |
| "Ping src: %pI6 " |
| "dest: %pI6\n", __func__, |
| &ha->ip_config.ipv6_addr1, |
| ipaddr)); |
| } |
| rval = qla4xxx_ping_iocb(ha, options, payload_size, |
| pid, ipaddr); |
| if (rval) |
| rval = -EINVAL; |
| } |
| } else |
| rval = -ENOSYS; |
| exit_send_ping: |
| return rval; |
| } |
| |
| static umode_t qla4_attr_is_visible(int param_type, int param) |
| { |
| switch (param_type) { |
| case ISCSI_HOST_PARAM: |
| switch (param) { |
| case ISCSI_HOST_PARAM_HWADDRESS: |
| case ISCSI_HOST_PARAM_IPADDRESS: |
| case ISCSI_HOST_PARAM_INITIATOR_NAME: |
| case ISCSI_HOST_PARAM_PORT_STATE: |
| case ISCSI_HOST_PARAM_PORT_SPEED: |
| return S_IRUGO; |
| default: |
| return 0; |
| } |
| case ISCSI_PARAM: |
| switch (param) { |
| case ISCSI_PARAM_PERSISTENT_ADDRESS: |
| case ISCSI_PARAM_PERSISTENT_PORT: |
| case ISCSI_PARAM_CONN_ADDRESS: |
| case ISCSI_PARAM_CONN_PORT: |
| case ISCSI_PARAM_TARGET_NAME: |
| case ISCSI_PARAM_TPGT: |
| case ISCSI_PARAM_TARGET_ALIAS: |
| case ISCSI_PARAM_MAX_BURST: |
| case ISCSI_PARAM_MAX_R2T: |
| case ISCSI_PARAM_FIRST_BURST: |
| case ISCSI_PARAM_MAX_RECV_DLENGTH: |
| case ISCSI_PARAM_MAX_XMIT_DLENGTH: |
| case ISCSI_PARAM_IFACE_NAME: |
| case ISCSI_PARAM_CHAP_OUT_IDX: |
| case ISCSI_PARAM_CHAP_IN_IDX: |
| case ISCSI_PARAM_USERNAME: |
| case ISCSI_PARAM_PASSWORD: |
| case ISCSI_PARAM_USERNAME_IN: |
| case ISCSI_PARAM_PASSWORD_IN: |
| case ISCSI_PARAM_AUTO_SND_TGT_DISABLE: |
| case ISCSI_PARAM_DISCOVERY_SESS: |
| case ISCSI_PARAM_PORTAL_TYPE: |
| case ISCSI_PARAM_CHAP_AUTH_EN: |
| case ISCSI_PARAM_DISCOVERY_LOGOUT_EN: |
| case ISCSI_PARAM_BIDI_CHAP_EN: |
| case ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL: |
| case ISCSI_PARAM_DEF_TIME2WAIT: |
| case ISCSI_PARAM_DEF_TIME2RETAIN: |
| case ISCSI_PARAM_HDRDGST_EN: |
| case ISCSI_PARAM_DATADGST_EN: |
| case ISCSI_PARAM_INITIAL_R2T_EN: |
| case ISCSI_PARAM_IMM_DATA_EN: |
| case ISCSI_PARAM_PDU_INORDER_EN: |
| case ISCSI_PARAM_DATASEQ_INORDER_EN: |
| case ISCSI_PARAM_MAX_SEGMENT_SIZE: |
| case ISCSI_PARAM_TCP_TIMESTAMP_STAT: |
| case ISCSI_PARAM_TCP_WSF_DISABLE: |
| case ISCSI_PARAM_TCP_NAGLE_DISABLE: |
| case ISCSI_PARAM_TCP_TIMER_SCALE: |
| case ISCSI_PARAM_TCP_TIMESTAMP_EN: |
| case ISCSI_PARAM_TCP_XMIT_WSF: |
| case ISCSI_PARAM_TCP_RECV_WSF: |
| case ISCSI_PARAM_IP_FRAGMENT_DISABLE: |
| case ISCSI_PARAM_IPV4_TOS: |
| case ISCSI_PARAM_IPV6_TC: |
| case ISCSI_PARAM_IPV6_FLOW_LABEL: |
| case ISCSI_PARAM_IS_FW_ASSIGNED_IPV6: |
| case ISCSI_PARAM_KEEPALIVE_TMO: |
| case ISCSI_PARAM_LOCAL_PORT: |
| case ISCSI_PARAM_ISID: |
| case ISCSI_PARAM_TSID: |
| case ISCSI_PARAM_DEF_TASKMGMT_TMO: |
| case ISCSI_PARAM_ERL: |
| case ISCSI_PARAM_STATSN: |
| case ISCSI_PARAM_EXP_STATSN: |
| case ISCSI_PARAM_DISCOVERY_PARENT_IDX: |
| case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: |
| case ISCSI_PARAM_LOCAL_IPADDR: |
| return S_IRUGO; |
| default: |
| return 0; |
| } |
| case ISCSI_NET_PARAM: |
| switch (param) { |
| case ISCSI_NET_PARAM_IPV4_ADDR: |
| case ISCSI_NET_PARAM_IPV4_SUBNET: |
| case ISCSI_NET_PARAM_IPV4_GW: |
| case ISCSI_NET_PARAM_IPV4_BOOTPROTO: |
| case ISCSI_NET_PARAM_IFACE_ENABLE: |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL: |
| case ISCSI_NET_PARAM_IPV6_ADDR: |
| case ISCSI_NET_PARAM_IPV6_ROUTER: |
| case ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG: |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG: |
| case ISCSI_NET_PARAM_VLAN_ID: |
| case ISCSI_NET_PARAM_VLAN_PRIORITY: |
| case ISCSI_NET_PARAM_VLAN_ENABLED: |
| case ISCSI_NET_PARAM_MTU: |
| case ISCSI_NET_PARAM_PORT: |
| case ISCSI_NET_PARAM_IPADDR_STATE: |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE: |
| case ISCSI_NET_PARAM_IPV6_ROUTER_STATE: |
| case ISCSI_NET_PARAM_DELAYED_ACK_EN: |
| case ISCSI_NET_PARAM_TCP_NAGLE_DISABLE: |
| case ISCSI_NET_PARAM_TCP_WSF_DISABLE: |
| case ISCSI_NET_PARAM_TCP_WSF: |
| case ISCSI_NET_PARAM_TCP_TIMER_SCALE: |
| case ISCSI_NET_PARAM_TCP_TIMESTAMP_EN: |
| case ISCSI_NET_PARAM_CACHE_ID: |
| case ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN: |
| case ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN: |
| case ISCSI_NET_PARAM_IPV4_TOS_EN: |
| case ISCSI_NET_PARAM_IPV4_TOS: |
| case ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN: |
| case ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN: |
| case ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID: |
| case ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN: |
| case ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN: |
| case ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID: |
| case ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN: |
| case ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE: |
| case ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN: |
| case ISCSI_NET_PARAM_REDIRECT_EN: |
| case ISCSI_NET_PARAM_IPV4_TTL: |
| case ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN: |
| case ISCSI_NET_PARAM_IPV6_MLD_EN: |
| case ISCSI_NET_PARAM_IPV6_FLOW_LABEL: |
| case ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS: |
| case ISCSI_NET_PARAM_IPV6_HOP_LIMIT: |
| case ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO: |
| case ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME: |
| case ISCSI_NET_PARAM_IPV6_ND_STALE_TMO: |
| case ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT: |
| case ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU: |
| return S_IRUGO; |
| default: |
| return 0; |
| } |
| case ISCSI_IFACE_PARAM: |
| switch (param) { |
| case ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO: |
| case ISCSI_IFACE_PARAM_HDRDGST_EN: |
| case ISCSI_IFACE_PARAM_DATADGST_EN: |
| case ISCSI_IFACE_PARAM_IMM_DATA_EN: |
| case ISCSI_IFACE_PARAM_INITIAL_R2T_EN: |
| case ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN: |
| case ISCSI_IFACE_PARAM_PDU_INORDER_EN: |
| case ISCSI_IFACE_PARAM_ERL: |
| case ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH: |
| case ISCSI_IFACE_PARAM_FIRST_BURST: |
| case ISCSI_IFACE_PARAM_MAX_R2T: |
| case ISCSI_IFACE_PARAM_MAX_BURST: |
| case ISCSI_IFACE_PARAM_CHAP_AUTH_EN: |
| case ISCSI_IFACE_PARAM_BIDI_CHAP_EN: |
| case ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL: |
| case ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN: |
| case ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN: |
| case ISCSI_IFACE_PARAM_INITIATOR_NAME: |
| return S_IRUGO; |
| default: |
| return 0; |
| } |
| case ISCSI_FLASHNODE_PARAM: |
| switch (param) { |
| case ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6: |
| case ISCSI_FLASHNODE_PORTAL_TYPE: |
| case ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE: |
| case ISCSI_FLASHNODE_DISCOVERY_SESS: |
| case ISCSI_FLASHNODE_ENTRY_EN: |
| case ISCSI_FLASHNODE_HDR_DGST_EN: |
| case ISCSI_FLASHNODE_DATA_DGST_EN: |
| case ISCSI_FLASHNODE_IMM_DATA_EN: |
| case ISCSI_FLASHNODE_INITIAL_R2T_EN: |
| case ISCSI_FLASHNODE_DATASEQ_INORDER: |
| case ISCSI_FLASHNODE_PDU_INORDER: |
| case ISCSI_FLASHNODE_CHAP_AUTH_EN: |
| case ISCSI_FLASHNODE_SNACK_REQ_EN: |
| case ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN: |
| case ISCSI_FLASHNODE_BIDI_CHAP_EN: |
| case ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL: |
| case ISCSI_FLASHNODE_ERL: |
| case ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT: |
| case ISCSI_FLASHNODE_TCP_NAGLE_DISABLE: |
| case ISCSI_FLASHNODE_TCP_WSF_DISABLE: |
| case ISCSI_FLASHNODE_TCP_TIMER_SCALE: |
| case ISCSI_FLASHNODE_TCP_TIMESTAMP_EN: |
| case ISCSI_FLASHNODE_IP_FRAG_DISABLE: |
| case ISCSI_FLASHNODE_MAX_RECV_DLENGTH: |
| case ISCSI_FLASHNODE_MAX_XMIT_DLENGTH: |
| case ISCSI_FLASHNODE_FIRST_BURST: |
| case ISCSI_FLASHNODE_DEF_TIME2WAIT: |
| case ISCSI_FLASHNODE_DEF_TIME2RETAIN: |
| case ISCSI_FLASHNODE_MAX_R2T: |
| case ISCSI_FLASHNODE_KEEPALIVE_TMO: |
| case ISCSI_FLASHNODE_ISID: |
| case ISCSI_FLASHNODE_TSID: |
| case ISCSI_FLASHNODE_PORT: |
| case ISCSI_FLASHNODE_MAX_BURST: |
| case ISCSI_FLASHNODE_DEF_TASKMGMT_TMO: |
| case ISCSI_FLASHNODE_IPADDR: |
| case ISCSI_FLASHNODE_ALIAS: |
| case ISCSI_FLASHNODE_REDIRECT_IPADDR: |
| case ISCSI_FLASHNODE_MAX_SEGMENT_SIZE: |
| case ISCSI_FLASHNODE_LOCAL_PORT: |
| case ISCSI_FLASHNODE_IPV4_TOS: |
| case ISCSI_FLASHNODE_IPV6_TC: |
| case ISCSI_FLASHNODE_IPV6_FLOW_LABEL: |
| case ISCSI_FLASHNODE_NAME: |
| case ISCSI_FLASHNODE_TPGT: |
| case ISCSI_FLASHNODE_LINK_LOCAL_IPV6: |
| case ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX: |
| case ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE: |
| case ISCSI_FLASHNODE_TCP_XMIT_WSF: |
| case ISCSI_FLASHNODE_TCP_RECV_WSF: |
| case ISCSI_FLASHNODE_CHAP_OUT_IDX: |
| case ISCSI_FLASHNODE_USERNAME: |
| case ISCSI_FLASHNODE_PASSWORD: |
| case ISCSI_FLASHNODE_STATSN: |
| case ISCSI_FLASHNODE_EXP_STATSN: |
| case ISCSI_FLASHNODE_IS_BOOT_TGT: |
| return S_IRUGO; |
| default: |
| return 0; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * qla4xxx_create chap_list - Create CHAP list from FLASH |
| * @ha: pointer to adapter structure |
| * |
| * Read flash and make a list of CHAP entries, during login when a CHAP entry |
| * is received, it will be checked in this list. If entry exist then the CHAP |
| * entry index is set in the DDB. If CHAP entry does not exist in this list |
| * then a new entry is added in FLASH in CHAP table and the index obtained is |
| * used in the DDB. |
| **/ |
| static void qla4xxx_create_chap_list(struct scsi_qla_host *ha) |
| { |
| int rval = 0; |
| uint8_t *chap_flash_data = NULL; |
| uint32_t offset; |
| dma_addr_t chap_dma; |
| uint32_t chap_size = 0; |
| |
| if (is_qla40XX(ha)) |
| chap_size = MAX_CHAP_ENTRIES_40XX * |
| sizeof(struct ql4_chap_table); |
| else /* Single region contains CHAP info for both |
| * ports which is divided into half for each port. |
| */ |
| chap_size = ha->hw.flt_chap_size / 2; |
| |
| chap_flash_data = dma_alloc_coherent(&ha->pdev->dev, chap_size, |
| &chap_dma, GFP_KERNEL); |
| if (!chap_flash_data) { |
| ql4_printk(KERN_ERR, ha, "No memory for chap_flash_data\n"); |
| return; |
| } |
| |
| if (is_qla40XX(ha)) { |
| offset = FLASH_CHAP_OFFSET; |
| } else { |
| offset = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_region_chap << 2); |
| if (ha->port_num == 1) |
| offset += chap_size; |
| } |
| |
| rval = qla4xxx_get_flash(ha, chap_dma, offset, chap_size); |
| if (rval != QLA_SUCCESS) |
| goto exit_chap_list; |
| |
| if (ha->chap_list == NULL) |
| ha->chap_list = vmalloc(chap_size); |
| if (ha->chap_list == NULL) { |
| ql4_printk(KERN_ERR, ha, "No memory for ha->chap_list\n"); |
| goto exit_chap_list; |
| } |
| |
| memset(ha->chap_list, 0, chap_size); |
| memcpy(ha->chap_list, chap_flash_data, chap_size); |
| |
| exit_chap_list: |
| dma_free_coherent(&ha->pdev->dev, chap_size, chap_flash_data, chap_dma); |
| } |
| |
| static int qla4xxx_get_chap_by_index(struct scsi_qla_host *ha, |
| int16_t chap_index, |
| struct ql4_chap_table **chap_entry) |
| { |
| int rval = QLA_ERROR; |
| int max_chap_entries; |
| |
| if (!ha->chap_list) { |
| ql4_printk(KERN_ERR, ha, "CHAP table cache is empty!\n"); |
| rval = QLA_ERROR; |
| goto exit_get_chap; |
| } |
| |
| if (is_qla80XX(ha)) |
| max_chap_entries = (ha->hw.flt_chap_size / 2) / |
| sizeof(struct ql4_chap_table); |
| else |
| max_chap_entries = MAX_CHAP_ENTRIES_40XX; |
| |
| if (chap_index > max_chap_entries) { |
| ql4_printk(KERN_ERR, ha, "Invalid Chap index\n"); |
| rval = QLA_ERROR; |
| goto exit_get_chap; |
| } |
| |
| *chap_entry = (struct ql4_chap_table *)ha->chap_list + chap_index; |
| if ((*chap_entry)->cookie != |
| __constant_cpu_to_le16(CHAP_VALID_COOKIE)) { |
| rval = QLA_ERROR; |
| *chap_entry = NULL; |
| } else { |
| rval = QLA_SUCCESS; |
| } |
| |
| exit_get_chap: |
| return rval; |
| } |
| |
| /** |
| * qla4xxx_find_free_chap_index - Find the first free chap index |
| * @ha: pointer to adapter structure |
| * @chap_index: CHAP index to be returned |
| * |
| * Find the first free chap index available in the chap table |
| * |
| * Note: Caller should acquire the chap lock before getting here. |
| **/ |
| static int qla4xxx_find_free_chap_index(struct scsi_qla_host *ha, |
| uint16_t *chap_index) |
| { |
| int i, rval; |
| int free_index = -1; |
| int max_chap_entries = 0; |
| struct ql4_chap_table *chap_table; |
| |
| if (is_qla80XX(ha)) |
| max_chap_entries = (ha->hw.flt_chap_size / 2) / |
| sizeof(struct ql4_chap_table); |
| else |
| max_chap_entries = MAX_CHAP_ENTRIES_40XX; |
| |
| if (!ha->chap_list) { |
| ql4_printk(KERN_ERR, ha, "CHAP table cache is empty!\n"); |
| rval = QLA_ERROR; |
| goto exit_find_chap; |
| } |
| |
| for (i = 0; i < max_chap_entries; i++) { |
| chap_table = (struct ql4_chap_table *)ha->chap_list + i; |
| |
| if ((chap_table->cookie != |
| __constant_cpu_to_le16(CHAP_VALID_COOKIE)) && |
| (i > MAX_RESRV_CHAP_IDX)) { |
| free_index = i; |
| break; |
| } |
| } |
| |
| if (free_index != -1) { |
| *chap_index = free_index; |
| rval = QLA_SUCCESS; |
| } else { |
| rval = QLA_ERROR; |
| } |
| |
| exit_find_chap: |
| return rval; |
| } |
| |
| static int qla4xxx_get_chap_list(struct Scsi_Host *shost, uint16_t chap_tbl_idx, |
| uint32_t *num_entries, char *buf) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| struct ql4_chap_table *chap_table; |
| struct iscsi_chap_rec *chap_rec; |
| int max_chap_entries = 0; |
| int valid_chap_entries = 0; |
| int ret = 0, i; |
| |
| if (is_qla80XX(ha)) |
| max_chap_entries = (ha->hw.flt_chap_size / 2) / |
| sizeof(struct ql4_chap_table); |
| else |
| max_chap_entries = MAX_CHAP_ENTRIES_40XX; |
| |
| ql4_printk(KERN_INFO, ha, "%s: num_entries = %d, CHAP idx = %d\n", |
| __func__, *num_entries, chap_tbl_idx); |
| |
| if (!buf) { |
| ret = -ENOMEM; |
| goto exit_get_chap_list; |
| } |
| |
| qla4xxx_create_chap_list(ha); |
| |
| chap_rec = (struct iscsi_chap_rec *) buf; |
| mutex_lock(&ha->chap_sem); |
| for (i = chap_tbl_idx; i < max_chap_entries; i++) { |
| chap_table = (struct ql4_chap_table *)ha->chap_list + i; |
| if (chap_table->cookie != |
| __constant_cpu_to_le16(CHAP_VALID_COOKIE)) |
| continue; |
| |
| chap_rec->chap_tbl_idx = i; |
| strlcpy(chap_rec->username, chap_table->name, |
| ISCSI_CHAP_AUTH_NAME_MAX_LEN); |
| strlcpy(chap_rec->password, chap_table->secret, |
| QL4_CHAP_MAX_SECRET_LEN); |
| chap_rec->password_length = chap_table->secret_len; |
| |
| if (chap_table->flags & BIT_7) /* local */ |
| chap_rec->chap_type = CHAP_TYPE_OUT; |
| |
| if (chap_table->flags & BIT_6) /* peer */ |
| chap_rec->chap_type = CHAP_TYPE_IN; |
| |
| chap_rec++; |
| |
| valid_chap_entries++; |
| if (valid_chap_entries == *num_entries) |
| break; |
| else |
| continue; |
| } |
| mutex_unlock(&ha->chap_sem); |
| |
| exit_get_chap_list: |
| ql4_printk(KERN_INFO, ha, "%s: Valid CHAP Entries = %d\n", |
| __func__, valid_chap_entries); |
| *num_entries = valid_chap_entries; |
| return ret; |
| } |
| |
| static int __qla4xxx_is_chap_active(struct device *dev, void *data) |
| { |
| int ret = 0; |
| uint16_t *chap_tbl_idx = (uint16_t *) data; |
| struct iscsi_cls_session *cls_session; |
| struct iscsi_session *sess; |
| struct ddb_entry *ddb_entry; |
| |
| if (!iscsi_is_session_dev(dev)) |
| goto exit_is_chap_active; |
| |
| cls_session = iscsi_dev_to_session(dev); |
| sess = cls_session->dd_data; |
| ddb_entry = sess->dd_data; |
| |
| if (iscsi_session_chkready(cls_session)) |
| goto exit_is_chap_active; |
| |
| if (ddb_entry->chap_tbl_idx == *chap_tbl_idx) |
| ret = 1; |
| |
| exit_is_chap_active: |
| return ret; |
| } |
| |
| static int qla4xxx_is_chap_active(struct Scsi_Host *shost, |
| uint16_t chap_tbl_idx) |
| { |
| int ret = 0; |
| |
| ret = device_for_each_child(&shost->shost_gendev, &chap_tbl_idx, |
| __qla4xxx_is_chap_active); |
| |
| return ret; |
| } |
| |
| static int qla4xxx_delete_chap(struct Scsi_Host *shost, uint16_t chap_tbl_idx) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| struct ql4_chap_table *chap_table; |
| dma_addr_t chap_dma; |
| int max_chap_entries = 0; |
| uint32_t offset = 0; |
| uint32_t chap_size; |
| int ret = 0; |
| |
| chap_table = dma_pool_alloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma); |
| if (chap_table == NULL) |
| return -ENOMEM; |
| |
| memset(chap_table, 0, sizeof(struct ql4_chap_table)); |
| |
| if (is_qla80XX(ha)) |
| max_chap_entries = (ha->hw.flt_chap_size / 2) / |
| sizeof(struct ql4_chap_table); |
| else |
| max_chap_entries = MAX_CHAP_ENTRIES_40XX; |
| |
| if (chap_tbl_idx > max_chap_entries) { |
| ret = -EINVAL; |
| goto exit_delete_chap; |
| } |
| |
| /* Check if chap index is in use. |
| * If chap is in use don't delet chap entry */ |
| ret = qla4xxx_is_chap_active(shost, chap_tbl_idx); |
| if (ret) { |
| ql4_printk(KERN_INFO, ha, "CHAP entry %d is in use, cannot " |
| "delete from flash\n", chap_tbl_idx); |
| ret = -EBUSY; |
| goto exit_delete_chap; |
| } |
| |
| chap_size = sizeof(struct ql4_chap_table); |
| if (is_qla40XX(ha)) |
| offset = FLASH_CHAP_OFFSET | (chap_tbl_idx * chap_size); |
| else { |
| offset = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_region_chap << 2); |
| /* flt_chap_size is CHAP table size for both ports |
| * so divide it by 2 to calculate the offset for second port |
| */ |
| if (ha->port_num == 1) |
| offset += (ha->hw.flt_chap_size / 2); |
| offset += (chap_tbl_idx * chap_size); |
| } |
| |
| ret = qla4xxx_get_flash(ha, chap_dma, offset, chap_size); |
| if (ret != QLA_SUCCESS) { |
| ret = -EINVAL; |
| goto exit_delete_chap; |
| } |
| |
| DEBUG2(ql4_printk(KERN_INFO, ha, "Chap Cookie: x%x\n", |
| __le16_to_cpu(chap_table->cookie))); |
| |
| if (__le16_to_cpu(chap_table->cookie) != CHAP_VALID_COOKIE) { |
| ql4_printk(KERN_ERR, ha, "No valid chap entry found\n"); |
| goto exit_delete_chap; |
| } |
| |
| chap_table->cookie = __constant_cpu_to_le16(0xFFFF); |
| |
| offset = FLASH_CHAP_OFFSET | |
| (chap_tbl_idx * sizeof(struct ql4_chap_table)); |
| ret = qla4xxx_set_flash(ha, chap_dma, offset, chap_size, |
| FLASH_OPT_RMW_COMMIT); |
| if (ret == QLA_SUCCESS && ha->chap_list) { |
| mutex_lock(&ha->chap_sem); |
| /* Update ha chap_list cache */ |
| memcpy((struct ql4_chap_table *)ha->chap_list + chap_tbl_idx, |
| chap_table, sizeof(struct ql4_chap_table)); |
| mutex_unlock(&ha->chap_sem); |
| } |
| if (ret != QLA_SUCCESS) |
| ret = -EINVAL; |
| |
| exit_delete_chap: |
| dma_pool_free(ha->chap_dma_pool, chap_table, chap_dma); |
| return ret; |
| } |
| |
| /** |
| * qla4xxx_set_chap_entry - Make chap entry with given information |
| * @shost: pointer to host |
| * @data: chap info - credentials, index and type to make chap entry |
| * @len: length of data |
| * |
| * Add or update chap entry with the given information |
| **/ |
| static int qla4xxx_set_chap_entry(struct Scsi_Host *shost, void *data, int len) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| struct iscsi_chap_rec chap_rec; |
| struct ql4_chap_table *chap_entry = NULL; |
| struct iscsi_param_info *param_info; |
| struct nlattr *attr; |
| int max_chap_entries = 0; |
| int type; |
| int rem = len; |
| int rc = 0; |
| int size; |
| |
| memset(&chap_rec, 0, sizeof(chap_rec)); |
| |
| nla_for_each_attr(attr, data, len, rem) { |
| param_info = nla_data(attr); |
| |
| switch (param_info->param) { |
| case ISCSI_CHAP_PARAM_INDEX: |
| chap_rec.chap_tbl_idx = *(uint16_t *)param_info->value; |
| break; |
| case ISCSI_CHAP_PARAM_CHAP_TYPE: |
| chap_rec.chap_type = param_info->value[0]; |
| break; |
| case ISCSI_CHAP_PARAM_USERNAME: |
| size = min_t(size_t, sizeof(chap_rec.username), |
| param_info->len); |
| memcpy(chap_rec.username, param_info->value, size); |
| break; |
| case ISCSI_CHAP_PARAM_PASSWORD: |
| size = min_t(size_t, sizeof(chap_rec.password), |
| param_info->len); |
| memcpy(chap_rec.password, param_info->value, size); |
| break; |
| case ISCSI_CHAP_PARAM_PASSWORD_LEN: |
| chap_rec.password_length = param_info->value[0]; |
| break; |
| default: |
| ql4_printk(KERN_ERR, ha, |
| "%s: No such sysfs attribute\n", __func__); |
| rc = -ENOSYS; |
| goto exit_set_chap; |
| }; |
| } |
| |
| if (chap_rec.chap_type == CHAP_TYPE_IN) |
| type = BIDI_CHAP; |
| else |
| type = LOCAL_CHAP; |
| |
| if (is_qla80XX(ha)) |
| max_chap_entries = (ha->hw.flt_chap_size / 2) / |
| sizeof(struct ql4_chap_table); |
| else |
| max_chap_entries = MAX_CHAP_ENTRIES_40XX; |
| |
| mutex_lock(&ha->chap_sem); |
| if (chap_rec.chap_tbl_idx < max_chap_entries) { |
| rc = qla4xxx_get_chap_by_index(ha, chap_rec.chap_tbl_idx, |
| &chap_entry); |
| if (!rc) { |
| if (!(type == qla4xxx_get_chap_type(chap_entry))) { |
| ql4_printk(KERN_INFO, ha, |
| "Type mismatch for CHAP entry %d\n", |
| chap_rec.chap_tbl_idx); |
| rc = -EINVAL; |
| goto exit_unlock_chap; |
| } |
| |
| /* If chap index is in use then don't modify it */ |
| rc = qla4xxx_is_chap_active(shost, |
| chap_rec.chap_tbl_idx); |
| if (rc) { |
| ql4_printk(KERN_INFO, ha, |
| "CHAP entry %d is in use\n", |
| chap_rec.chap_tbl_idx); |
| rc = -EBUSY; |
| goto exit_unlock_chap; |
| } |
| } |
| } else { |
| rc = qla4xxx_find_free_chap_index(ha, &chap_rec.chap_tbl_idx); |
| if (rc) { |
| ql4_printk(KERN_INFO, ha, "CHAP entry not available\n"); |
| rc = -EBUSY; |
| goto exit_unlock_chap; |
| } |
| } |
| |
| rc = qla4xxx_set_chap(ha, chap_rec.username, chap_rec.password, |
| chap_rec.chap_tbl_idx, type); |
| |
| exit_unlock_chap: |
| mutex_unlock(&ha->chap_sem); |
| |
| exit_set_chap: |
| return rc; |
| } |
| |
| |
| static int qla4xxx_get_host_stats(struct Scsi_Host *shost, char *buf, int len) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| struct iscsi_offload_host_stats *host_stats = NULL; |
| int host_stats_size; |
| int ret = 0; |
| int ddb_idx = 0; |
| struct ql_iscsi_stats *ql_iscsi_stats = NULL; |
| int stats_size; |
| dma_addr_t iscsi_stats_dma; |
| |
| DEBUG2(ql4_printk(KERN_INFO, ha, "Func: %s\n", __func__)); |
| |
| host_stats_size = sizeof(struct iscsi_offload_host_stats); |
| |
| if (host_stats_size != len) { |
| ql4_printk(KERN_INFO, ha, "%s: host_stats size mismatch expected = %d, is = %d\n", |
| __func__, len, host_stats_size); |
| ret = -EINVAL; |
| goto exit_host_stats; |
| } |
| host_stats = (struct iscsi_offload_host_stats *)buf; |
| |
| if (!buf) { |
| ret = -ENOMEM; |
| goto exit_host_stats; |
| } |
| |
| stats_size = PAGE_ALIGN(sizeof(struct ql_iscsi_stats)); |
| |
| ql_iscsi_stats = dma_alloc_coherent(&ha->pdev->dev, stats_size, |
| &iscsi_stats_dma, GFP_KERNEL); |
| if (!ql_iscsi_stats) { |
| ql4_printk(KERN_ERR, ha, |
| "Unable to allocate memory for iscsi stats\n"); |
| ret = -ENOMEM; |
| goto exit_host_stats; |
| } |
| |
| ret = qla4xxx_get_mgmt_data(ha, ddb_idx, stats_size, |
| iscsi_stats_dma); |
| if (ret != QLA_SUCCESS) { |
| ql4_printk(KERN_ERR, ha, |
| "Unable to retrieve iscsi stats\n"); |
| ret = -EIO; |
| goto exit_host_stats; |
| } |
| host_stats->mactx_frames = le64_to_cpu(ql_iscsi_stats->mac_tx_frames); |
| host_stats->mactx_bytes = le64_to_cpu(ql_iscsi_stats->mac_tx_bytes); |
| host_stats->mactx_multicast_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_multicast_frames); |
| host_stats->mactx_broadcast_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_broadcast_frames); |
| host_stats->mactx_pause_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_pause_frames); |
| host_stats->mactx_control_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_control_frames); |
| host_stats->mactx_deferral = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_deferral); |
| host_stats->mactx_excess_deferral = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_excess_deferral); |
| host_stats->mactx_late_collision = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_late_collision); |
| host_stats->mactx_abort = le64_to_cpu(ql_iscsi_stats->mac_tx_abort); |
| host_stats->mactx_single_collision = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_single_collision); |
| host_stats->mactx_multiple_collision = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_multiple_collision); |
| host_stats->mactx_collision = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_collision); |
| host_stats->mactx_frames_dropped = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_frames_dropped); |
| host_stats->mactx_jumbo_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_tx_jumbo_frames); |
| host_stats->macrx_frames = le64_to_cpu(ql_iscsi_stats->mac_rx_frames); |
| host_stats->macrx_bytes = le64_to_cpu(ql_iscsi_stats->mac_rx_bytes); |
| host_stats->macrx_unknown_control_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_unknown_control_frames); |
| host_stats->macrx_pause_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_pause_frames); |
| host_stats->macrx_control_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_control_frames); |
| host_stats->macrx_dribble = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_dribble); |
| host_stats->macrx_frame_length_error = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_frame_length_error); |
| host_stats->macrx_jabber = le64_to_cpu(ql_iscsi_stats->mac_rx_jabber); |
| host_stats->macrx_carrier_sense_error = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_carrier_sense_error); |
| host_stats->macrx_frame_discarded = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_frame_discarded); |
| host_stats->macrx_frames_dropped = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_frames_dropped); |
| host_stats->mac_crc_error = le64_to_cpu(ql_iscsi_stats->mac_crc_error); |
| host_stats->mac_encoding_error = |
| le64_to_cpu(ql_iscsi_stats->mac_encoding_error); |
| host_stats->macrx_length_error_large = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_length_error_large); |
| host_stats->macrx_length_error_small = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_length_error_small); |
| host_stats->macrx_multicast_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_multicast_frames); |
| host_stats->macrx_broadcast_frames = |
| le64_to_cpu(ql_iscsi_stats->mac_rx_broadcast_frames); |
| host_stats->iptx_packets = le64_to_cpu(ql_iscsi_stats->ip_tx_packets); |
| host_stats->iptx_bytes = le64_to_cpu(ql_iscsi_stats->ip_tx_bytes); |
| host_stats->iptx_fragments = |
| le64_to_cpu(ql_iscsi_stats->ip_tx_fragments); |
| host_stats->iprx_packets = le64_to_cpu(ql_iscsi_stats->ip_rx_packets); |
| host_stats->iprx_bytes = le64_to_cpu(ql_iscsi_stats->ip_rx_bytes); |
| host_stats->iprx_fragments = |
| le64_to_cpu(ql_iscsi_stats->ip_rx_fragments); |
| host_stats->ip_datagram_reassembly = |
| le64_to_cpu(ql_iscsi_stats->ip_datagram_reassembly); |
| host_stats->ip_invalid_address_error = |
| le64_to_cpu(ql_iscsi_stats->ip_invalid_address_error); |
| host_stats->ip_error_packets = |
| le64_to_cpu(ql_iscsi_stats->ip_error_packets); |
| host_stats->ip_fragrx_overlap = |
| le64_to_cpu(ql_iscsi_stats->ip_fragrx_overlap); |
| host_stats->ip_fragrx_outoforder = |
| le64_to_cpu(ql_iscsi_stats->ip_fragrx_outoforder); |
| host_stats->ip_datagram_reassembly_timeout = |
| le64_to_cpu(ql_iscsi_stats->ip_datagram_reassembly_timeout); |
| host_stats->ipv6tx_packets = |
| le64_to_cpu(ql_iscsi_stats->ipv6_tx_packets); |
| host_stats->ipv6tx_bytes = le64_to_cpu(ql_iscsi_stats->ipv6_tx_bytes); |
| host_stats->ipv6tx_fragments = |
| le64_to_cpu(ql_iscsi_stats->ipv6_tx_fragments); |
| host_stats->ipv6rx_packets = |
| le64_to_cpu(ql_iscsi_stats->ipv6_rx_packets); |
| host_stats->ipv6rx_bytes = le64_to_cpu(ql_iscsi_stats->ipv6_rx_bytes); |
| host_stats->ipv6rx_fragments = |
| le64_to_cpu(ql_iscsi_stats->ipv6_rx_fragments); |
| host_stats->ipv6_datagram_reassembly = |
| le64_to_cpu(ql_iscsi_stats->ipv6_datagram_reassembly); |
| host_stats->ipv6_invalid_address_error = |
| le64_to_cpu(ql_iscsi_stats->ipv6_invalid_address_error); |
| host_stats->ipv6_error_packets = |
| le64_to_cpu(ql_iscsi_stats->ipv6_error_packets); |
| host_stats->ipv6_fragrx_overlap = |
| le64_to_cpu(ql_iscsi_stats->ipv6_fragrx_overlap); |
| host_stats->ipv6_fragrx_outoforder = |
| le64_to_cpu(ql_iscsi_stats->ipv6_fragrx_outoforder); |
| host_stats->ipv6_datagram_reassembly_timeout = |
| le64_to_cpu(ql_iscsi_stats->ipv6_datagram_reassembly_timeout); |
| host_stats->tcptx_segments = |
| le64_to_cpu(ql_iscsi_stats->tcp_tx_segments); |
| host_stats->tcptx_bytes = le64_to_cpu(ql_iscsi_stats->tcp_tx_bytes); |
| host_stats->tcprx_segments = |
| le64_to_cpu(ql_iscsi_stats->tcp_rx_segments); |
| host_stats->tcprx_byte = le64_to_cpu(ql_iscsi_stats->tcp_rx_byte); |
| host_stats->tcp_duplicate_ack_retx = |
| le64_to_cpu(ql_iscsi_stats->tcp_duplicate_ack_retx); |
| host_stats->tcp_retx_timer_expired = |
| le64_to_cpu(ql_iscsi_stats->tcp_retx_timer_expired); |
| host_stats->tcprx_duplicate_ack = |
| le64_to_cpu(ql_iscsi_stats->tcp_rx_duplicate_ack); |
| host_stats->tcprx_pure_ackr = |
| le64_to_cpu(ql_iscsi_stats->tcp_rx_pure_ackr); |
| host_stats->tcptx_delayed_ack = |
| le64_to_cpu(ql_iscsi_stats->tcp_tx_delayed_ack); |
| host_stats->tcptx_pure_ack = |
| le64_to_cpu(ql_iscsi_stats->tcp_tx_pure_ack); |
| host_stats->tcprx_segment_error = |
| le64_to_cpu(ql_iscsi_stats->tcp_rx_segment_error); |
| host_stats->tcprx_segment_outoforder = |
| le64_to_cpu(ql_iscsi_stats->tcp_rx_segment_outoforder); |
| host_stats->tcprx_window_probe = |
| le64_to_cpu(ql_iscsi_stats->tcp_rx_window_probe); |
| host_stats->tcprx_window_update = |
| le64_to_cpu(ql_iscsi_stats->tcp_rx_window_update); |
| host_stats->tcptx_window_probe_persist = |
| le64_to_cpu(ql_iscsi_stats->tcp_tx_window_probe_persist); |
| host_stats->ecc_error_correction = |
| le64_to_cpu(ql_iscsi_stats->ecc_error_correction); |
| host_stats->iscsi_pdu_tx = le64_to_cpu(ql_iscsi_stats->iscsi_pdu_tx); |
| host_stats->iscsi_data_bytes_tx = |
| le64_to_cpu(ql_iscsi_stats->iscsi_data_bytes_tx); |
| host_stats->iscsi_pdu_rx = le64_to_cpu(ql_iscsi_stats->iscsi_pdu_rx); |
| host_stats->iscsi_data_bytes_rx = |
| le64_to_cpu(ql_iscsi_stats->iscsi_data_bytes_rx); |
| host_stats->iscsi_io_completed = |
| le64_to_cpu(ql_iscsi_stats->iscsi_io_completed); |
| host_stats->iscsi_unexpected_io_rx = |
| le64_to_cpu(ql_iscsi_stats->iscsi_unexpected_io_rx); |
| host_stats->iscsi_format_error = |
| le64_to_cpu(ql_iscsi_stats->iscsi_format_error); |
| host_stats->iscsi_hdr_digest_error = |
| le64_to_cpu(ql_iscsi_stats->iscsi_hdr_digest_error); |
| host_stats->iscsi_data_digest_error = |
| le64_to_cpu(ql_iscsi_stats->iscsi_data_digest_error); |
| host_stats->iscsi_sequence_error = |
| le64_to_cpu(ql_iscsi_stats->iscsi_sequence_error); |
| exit_host_stats: |
| if (ql_iscsi_stats) |
| dma_free_coherent(&ha->pdev->dev, host_stats_size, |
| ql_iscsi_stats, iscsi_stats_dma); |
| |
| ql4_printk(KERN_INFO, ha, "%s: Get host stats done\n", |
| __func__); |
| return ret; |
| } |
| |
| static int qla4xxx_get_iface_param(struct iscsi_iface *iface, |
| enum iscsi_param_type param_type, |
| int param, char *buf) |
| { |
| struct Scsi_Host *shost = iscsi_iface_to_shost(iface); |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| int ival; |
| char *pval = NULL; |
| int len = -ENOSYS; |
| |
| if (param_type == ISCSI_NET_PARAM) { |
| switch (param) { |
| case ISCSI_NET_PARAM_IPV4_ADDR: |
| len = sprintf(buf, "%pI4\n", &ha->ip_config.ip_address); |
| break; |
| case ISCSI_NET_PARAM_IPV4_SUBNET: |
| len = sprintf(buf, "%pI4\n", |
| &ha->ip_config.subnet_mask); |
| break; |
| case ISCSI_NET_PARAM_IPV4_GW: |
| len = sprintf(buf, "%pI4\n", &ha->ip_config.gateway); |
| break; |
| case ISCSI_NET_PARAM_IFACE_ENABLE: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_IPV4_PROTOCOL_ENABLE, pval); |
| } else { |
| OP_STATE(ha->ip_config.ipv6_options, |
| IPV6_OPT_IPV6_PROTOCOL_ENABLE, pval); |
| } |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_BOOTPROTO: |
| len = sprintf(buf, "%s\n", |
| (ha->ip_config.tcp_options & |
| TCPOPT_DHCP_ENABLE) ? |
| "dhcp" : "static"); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ADDR: |
| if (iface->iface_num == 0) |
| len = sprintf(buf, "%pI6\n", |
| &ha->ip_config.ipv6_addr0); |
| if (iface->iface_num == 1) |
| len = sprintf(buf, "%pI6\n", |
| &ha->ip_config.ipv6_addr1); |
| break; |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL: |
| len = sprintf(buf, "%pI6\n", |
| &ha->ip_config.ipv6_link_local_addr); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ROUTER: |
| len = sprintf(buf, "%pI6\n", |
| &ha->ip_config.ipv6_default_router_addr); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG: |
| pval = (ha->ip_config.ipv6_addl_options & |
| IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE) ? |
| "nd" : "static"; |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG: |
| pval = (ha->ip_config.ipv6_addl_options & |
| IPV6_ADDOPT_AUTOCONFIG_LINK_LOCAL_ADDR) ? |
| "auto" : "static"; |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_VLAN_ID: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) |
| ival = ha->ip_config.ipv4_vlan_tag & |
| ISCSI_MAX_VLAN_ID; |
| else |
| ival = ha->ip_config.ipv6_vlan_tag & |
| ISCSI_MAX_VLAN_ID; |
| |
| len = sprintf(buf, "%d\n", ival); |
| break; |
| case ISCSI_NET_PARAM_VLAN_PRIORITY: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) |
| ival = (ha->ip_config.ipv4_vlan_tag >> 13) & |
| ISCSI_MAX_VLAN_PRIORITY; |
| else |
| ival = (ha->ip_config.ipv6_vlan_tag >> 13) & |
| ISCSI_MAX_VLAN_PRIORITY; |
| |
| len = sprintf(buf, "%d\n", ival); |
| break; |
| case ISCSI_NET_PARAM_VLAN_ENABLED: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_VLAN_TAGGING_ENABLE, pval); |
| } else { |
| OP_STATE(ha->ip_config.ipv6_options, |
| IPV6_OPT_VLAN_TAGGING_ENABLE, pval); |
| } |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_MTU: |
| len = sprintf(buf, "%d\n", ha->ip_config.eth_mtu_size); |
| break; |
| case ISCSI_NET_PARAM_PORT: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv4_port); |
| else |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_port); |
| break; |
| case ISCSI_NET_PARAM_IPADDR_STATE: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| pval = iscsi_get_ipaddress_state_name( |
| ha->ip_config.ipv4_addr_state); |
| } else { |
| if (iface->iface_num == 0) |
| pval = iscsi_get_ipaddress_state_name( |
| ha->ip_config.ipv6_addr0_state); |
| else if (iface->iface_num == 1) |
| pval = iscsi_get_ipaddress_state_name( |
| ha->ip_config.ipv6_addr1_state); |
| } |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE: |
| pval = iscsi_get_ipaddress_state_name( |
| ha->ip_config.ipv6_link_local_state); |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ROUTER_STATE: |
| pval = iscsi_get_router_state_name( |
| ha->ip_config.ipv6_default_router_state); |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_DELAYED_ACK_EN: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| OP_STATE(~ha->ip_config.tcp_options, |
| TCPOPT_DELAYED_ACK_DISABLE, pval); |
| } else { |
| OP_STATE(~ha->ip_config.ipv6_tcp_options, |
| IPV6_TCPOPT_DELAYED_ACK_DISABLE, pval); |
| } |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_TCP_NAGLE_DISABLE: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| OP_STATE(~ha->ip_config.tcp_options, |
| TCPOPT_NAGLE_ALGO_DISABLE, pval); |
| } else { |
| OP_STATE(~ha->ip_config.ipv6_tcp_options, |
| IPV6_TCPOPT_NAGLE_ALGO_DISABLE, pval); |
| } |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_TCP_WSF_DISABLE: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| OP_STATE(~ha->ip_config.tcp_options, |
| TCPOPT_WINDOW_SCALE_DISABLE, pval); |
| } else { |
| OP_STATE(~ha->ip_config.ipv6_tcp_options, |
| IPV6_TCPOPT_WINDOW_SCALE_DISABLE, |
| pval); |
| } |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_TCP_WSF: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.tcp_wsf); |
| else |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_tcp_wsf); |
| break; |
| case ISCSI_NET_PARAM_TCP_TIMER_SCALE: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) |
| ival = (ha->ip_config.tcp_options & |
| TCPOPT_TIMER_SCALE) >> 1; |
| else |
| ival = (ha->ip_config.ipv6_tcp_options & |
| IPV6_TCPOPT_TIMER_SCALE) >> 1; |
| |
| len = sprintf(buf, "%d\n", ival); |
| break; |
| case ISCSI_NET_PARAM_TCP_TIMESTAMP_EN: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| OP_STATE(ha->ip_config.tcp_options, |
| TCPOPT_TIMESTAMP_ENABLE, pval); |
| } else { |
| OP_STATE(ha->ip_config.ipv6_tcp_options, |
| IPV6_TCPOPT_TIMESTAMP_EN, pval); |
| } |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_CACHE_ID: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv4_cache_id); |
| else |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_cache_id); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN: |
| OP_STATE(ha->ip_config.tcp_options, |
| TCPOPT_DNS_SERVER_IP_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN: |
| OP_STATE(ha->ip_config.tcp_options, |
| TCPOPT_SLP_DA_INFO_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_TOS_EN: |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_IPV4_TOS_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_TOS: |
| len = sprintf(buf, "%d\n", ha->ip_config.ipv4_tos); |
| break; |
| case ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN: |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_GRAT_ARP_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN: |
| OP_STATE(ha->ip_config.ipv4_options, IPOPT_ALT_CID_EN, |
| pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID: |
| pval = (ha->ip_config.ipv4_alt_cid_len) ? |
| (char *)ha->ip_config.ipv4_alt_cid : ""; |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN: |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_REQ_VID_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN: |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_USE_VID_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID: |
| pval = (ha->ip_config.ipv4_vid_len) ? |
| (char *)ha->ip_config.ipv4_vid : ""; |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN: |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_LEARN_IQN_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE: |
| OP_STATE(~ha->ip_config.ipv4_options, |
| IPOPT_FRAGMENTATION_DISABLE, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN: |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_IN_FORWARD_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_REDIRECT_EN: |
| if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
| OP_STATE(ha->ip_config.ipv4_options, |
| IPOPT_ARP_REDIRECT_EN, pval); |
| } else { |
| OP_STATE(ha->ip_config.ipv6_options, |
| IPV6_OPT_REDIRECT_EN, pval); |
| } |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV4_TTL: |
| len = sprintf(buf, "%d\n", ha->ip_config.ipv4_ttl); |
| break; |
| case ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN: |
| OP_STATE(ha->ip_config.ipv6_options, |
| IPV6_OPT_GRAT_NEIGHBOR_ADV_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV6_MLD_EN: |
| OP_STATE(ha->ip_config.ipv6_addl_options, |
| IPV6_ADDOPT_MLD_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_NET_PARAM_IPV6_FLOW_LABEL: |
| len = sprintf(buf, "%u\n", ha->ip_config.ipv6_flow_lbl); |
| break; |
| case ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_traffic_class); |
| break; |
| case ISCSI_NET_PARAM_IPV6_HOP_LIMIT: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_hop_limit); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_nd_reach_time); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_nd_rexmit_timer); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ND_STALE_TMO: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_nd_stale_timeout); |
| break; |
| case ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_dup_addr_detect_count); |
| break; |
| case ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.ipv6_gw_advrt_mtu); |
| break; |
| default: |
| len = -ENOSYS; |
| } |
| } else if (param_type == ISCSI_IFACE_PARAM) { |
| switch (param) { |
| case ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO: |
| len = sprintf(buf, "%d\n", ha->ip_config.def_timeout); |
| break; |
| case ISCSI_IFACE_PARAM_HDRDGST_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_HEADER_DIGEST_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_DATADGST_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_DATA_DIGEST_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_IMM_DATA_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_IMMEDIATE_DATA_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_INITIAL_R2T_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_INITIAL_R2T_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_DATA_SEQ_INORDER_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_PDU_INORDER_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_DATA_PDU_INORDER_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_ERL: |
| len = sprintf(buf, "%d\n", |
| (ha->ip_config.iscsi_options & |
| ISCSIOPTS_ERL)); |
| break; |
| case ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH: |
| len = sprintf(buf, "%u\n", |
| ha->ip_config.iscsi_max_pdu_size * |
| BYTE_UNITS); |
| break; |
| case ISCSI_IFACE_PARAM_FIRST_BURST: |
| len = sprintf(buf, "%u\n", |
| ha->ip_config.iscsi_first_burst_len * |
| BYTE_UNITS); |
| break; |
| case ISCSI_IFACE_PARAM_MAX_R2T: |
| len = sprintf(buf, "%d\n", |
| ha->ip_config.iscsi_max_outstnd_r2t); |
| break; |
| case ISCSI_IFACE_PARAM_MAX_BURST: |
| len = sprintf(buf, "%u\n", |
| ha->ip_config.iscsi_max_burst_len * |
| BYTE_UNITS); |
| break; |
| case ISCSI_IFACE_PARAM_CHAP_AUTH_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_CHAP_AUTH_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_BIDI_CHAP_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_BIDI_CHAP_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_DISCOVERY_AUTH_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_DISCOVERY_LOGOUT_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN: |
| OP_STATE(ha->ip_config.iscsi_options, |
| ISCSIOPTS_STRICT_LOGIN_COMP_EN, pval); |
| |
| len = sprintf(buf, "%s\n", pval); |
| break; |
| case ISCSI_IFACE_PARAM_INITIATOR_NAME: |
| len = sprintf(buf, "%s\n", ha->ip_config.iscsi_name); |
| break; |
| default: |
| len = -ENOSYS; |
| } |
| } |
| |
| return len; |
| } |
| |
| static struct iscsi_endpoint * |
| qla4xxx_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, |
| int non_blocking) |
| { |
| int ret; |
| struct iscsi_endpoint *ep; |
| struct qla_endpoint *qla_ep; |
| struct scsi_qla_host *ha; |
| struct sockaddr_in *addr; |
| struct sockaddr_in6 *addr6; |
| |
| if (!shost) { |
| ret = -ENXIO; |
| pr_err("%s: shost is NULL\n", __func__); |
| return ERR_PTR(ret); |
| } |
| |
| ha = iscsi_host_priv(shost); |
| ep = iscsi_create_endpoint(sizeof(struct qla_endpoint)); |
| if (!ep) { |
| ret = -ENOMEM; |
| return ERR_PTR(ret); |
| } |
| |
| qla_ep = ep->dd_data; |
| memset(qla_ep, 0, sizeof(struct qla_endpoint)); |
| if (dst_addr->sa_family == AF_INET) { |
| memcpy(&qla_ep->dst_addr, dst_addr, sizeof(struct sockaddr_in)); |
| addr = (struct sockaddr_in *)&qla_ep->dst_addr; |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: %pI4\n", __func__, |
| (char *)&addr->sin_addr)); |
| } else if (dst_addr->sa_family == AF_INET6) { |
| memcpy(&qla_ep->dst_addr, dst_addr, |
| sizeof(struct sockaddr_in6)); |
| addr6 = (struct sockaddr_in6 *)&qla_ep->dst_addr; |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: %pI6\n", __func__, |
| (char *)&addr6->sin6_addr)); |
| } else { |
| ql4_printk(KERN_WARNING, ha, "%s: Invalid endpoint\n", |
| __func__); |
| } |
| |
| qla_ep->host = shost; |
| |
| return ep; |
| } |
| |
| static int qla4xxx_ep_poll(struct iscsi_endpoint *ep, int timeout_ms) |
| { |
| struct qla_endpoint *qla_ep; |
| struct scsi_qla_host *ha; |
| int ret = 0; |
| |
| qla_ep = ep->dd_data; |
| ha = to_qla_host(qla_ep->host); |
| DEBUG2(pr_info_ratelimited("%s: host: %ld\n", __func__, ha->host_no)); |
| |
| if (adapter_up(ha) && !test_bit(AF_BUILD_DDB_LIST, &ha->flags)) |
| ret = 1; |
| |
| return ret; |
| } |
| |
| static void qla4xxx_ep_disconnect(struct iscsi_endpoint *ep) |
| { |
| struct qla_endpoint *qla_ep; |
| struct scsi_qla_host *ha; |
| |
| qla_ep = ep->dd_data; |
| ha = to_qla_host(qla_ep->host); |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: host: %ld\n", __func__, |
| ha->host_no)); |
| iscsi_destroy_endpoint(ep); |
| } |
| |
| static int qla4xxx_get_ep_param(struct iscsi_endpoint *ep, |
| enum iscsi_param param, |
| char *buf) |
| { |
| struct qla_endpoint *qla_ep = ep->dd_data; |
| struct sockaddr *dst_addr; |
| struct scsi_qla_host *ha; |
| |
| if (!qla_ep) |
| return -ENOTCONN; |
| |
| ha = to_qla_host(qla_ep->host); |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: host: %ld\n", __func__, |
| ha->host_no)); |
| |
| switch (param) { |
| case ISCSI_PARAM_CONN_PORT: |
| case ISCSI_PARAM_CONN_ADDRESS: |
| dst_addr = (struct sockaddr *)&qla_ep->dst_addr; |
| if (!dst_addr) |
| return -ENOTCONN; |
| |
| return iscsi_conn_get_addr_param((struct sockaddr_storage *) |
| &qla_ep->dst_addr, param, buf); |
| default: |
| return -ENOSYS; |
| } |
| } |
| |
| static void qla4xxx_conn_get_stats(struct iscsi_cls_conn *cls_conn, |
| struct iscsi_stats *stats) |
| { |
| struct iscsi_session *sess; |
| struct iscsi_cls_session *cls_sess; |
| struct ddb_entry *ddb_entry; |
| struct scsi_qla_host *ha; |
| struct ql_iscsi_stats *ql_iscsi_stats; |
| int stats_size; |
| int ret; |
| dma_addr_t iscsi_stats_dma; |
| |
| cls_sess = iscsi_conn_to_session(cls_conn); |
| sess = cls_sess->dd_data; |
| ddb_entry = sess->dd_data; |
| ha = ddb_entry->ha; |
| |
| DEBUG2(ql4_printk(KERN_INFO, ha, "%s: host: %ld\n", __func__, |
| ha->host_no)); |
| stats_size = PAGE_ALIGN(sizeof(struct ql_iscsi_stats)); |
| /* Allocate memory */ |
| ql_iscsi_stats = dma_alloc_coherent(&ha->pdev->dev, stats_size, |
| &iscsi_stats_dma, GFP_KERNEL); |
| if (!ql_iscsi_stats) { |
| ql4_printk(KERN_ERR, ha, |
| "Unable to allocate memory for iscsi stats\n"); |
| goto exit_get_stats; |
| } |
| |
| ret = qla4xxx_get_mgmt_data(ha, ddb_entry->fw_ddb_index, stats_size, |
| iscsi_stats_dma); |
| if (ret != QLA_SUCCESS) { |
| ql4_printk(KERN_ERR, ha, |
| "Unable to retrieve iscsi stats\n"); |
| goto free_stats; |
| } |
| |
| /* octets */ |
| stats->txdata_octets = le64_to_cpu(ql_iscsi_stats->tx_data_octets); |
| stats->rxdata_octets = le64_to_cpu(ql_iscsi_stats->rx_data_octets); |
| /* xmit pdus */ |
| stats->noptx_pdus = le32_to_cpu(ql_iscsi_stats->tx_nopout_pdus); |
| stats->scsicmd_pdus = le32_to_cpu(ql_iscsi_stats->tx_scsi_cmd_pdus); |
| stats->tmfcmd_pdus = le32_to_cpu(ql_iscsi_stats->tx_tmf_cmd_pdus); |
| stats->login_pdus = le32_to_cpu(ql_iscsi_stats->tx_login_cmd_pdus); |
| stats->text_pdus = le32_to_cpu(ql_iscsi_stats->tx_text_cmd_pdus); |
| stats->dataout_pdus = le32_to_cpu(ql_iscsi_stats->tx_scsi_write_pdus); |
| stats->logout_pdus = le32_to_cpu(ql_iscsi_stats->tx_logout_cmd_pdus); |
| stats->snack_pdus = le32_to_cpu(ql_iscsi_stats->tx_snack_req_pdus); |
| /* recv pdus */ |
| stats->noprx_pdus = le32_to_cpu(ql_iscsi_stats->rx_nopin_pdus); |
| stats->scsirsp_pdus = le32_to_cpu(ql_iscsi_stats->rx_scsi_resp_pdus); |
| stats->tmfrsp_pdus = le32_to_cpu(ql_iscsi_stats->rx_tmf_resp_pdus); |
| stats->textrsp_pdus = le32_to_cpu(ql_iscsi_stats->rx_text_resp_pdus); |
| stats->datain_pdus = le32_to_cpu(ql_iscsi_stats->rx_scsi_read_pdus); |
| stats->logoutrsp_pdus = |
| le32_to_cpu(ql_iscsi_stats->rx_logout_resp_pdus); |
| stats->r2t_pdus = le32_to_cpu(ql_iscsi_stats->rx_r2t_pdus); |
| stats->async_pdus = le32_to_cpu(ql_iscsi_stats->rx_async_pdus); |
| stats->rjt_pdus = le32_to_cpu(ql_iscsi_stats->rx_reject_pdus); |
| |
| free_stats: |
| dma_free_coherent(&ha->pdev->dev, stats_size, ql_iscsi_stats, |
| iscsi_stats_dma); |
| exit_get_stats: |
| return; |
| } |
| |
| static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc) |
| { |
| struct iscsi_cls_session *session; |
| struct iscsi_session *sess; |
| unsigned long flags; |
| enum blk_eh_timer_return ret = BLK_EH_NOT_HANDLED; |
| |
| session = starget_to_session(scsi_target(sc->device)); |
| sess = session->dd_data; |
| |
| spin_lock_irqsave(&session->lock, flags); |
| if (session->state == ISCSI_SESSION_FAILED) |
| ret = BLK_EH_RESET_TIMER; |
| spin_unlock_irqrestore(&session->lock, flags); |
| |
| return ret; |
| } |
| |
| static void qla4xxx_set_port_speed(struct Scsi_Host *shost) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| struct iscsi_cls_host *ihost = shost->shost_data; |
| uint32_t speed = ISCSI_PORT_SPEED_UNKNOWN; |
| |
| qla4xxx_get_firmware_state(ha); |
| |
| switch (ha->addl_fw_state & 0x0F00) { |
| case FW_ADDSTATE_LINK_SPEED_10MBPS: |
| speed = ISCSI_PORT_SPEED_10MBPS; |
| break; |
| case FW_ADDSTATE_LINK_SPEED_100MBPS: |
| speed = ISCSI_PORT_SPEED_100MBPS; |
| break; |
| case FW_ADDSTATE_LINK_SPEED_1GBPS: |
| speed = ISCSI_PORT_SPEED_1GBPS; |
| break; |
| case FW_ADDSTATE_LINK_SPEED_10GBPS: |
| speed = ISCSI_PORT_SPEED_10GBPS; |
| break; |
| } |
| ihost->port_speed = speed; |
| } |
| |
| static void qla4xxx_set_port_state(struct Scsi_Host *shost) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| struct iscsi_cls_host *ihost = shost->shost_data; |
| uint32_t state = ISCSI_PORT_STATE_DOWN; |
| |
| if (test_bit(AF_LINK_UP, &ha->flags)) |
| state = ISCSI_PORT_STATE_UP; |
| |
| ihost->port_state = state; |
| } |
| |
| static int qla4xxx_host_get_param(struct Scsi_Host *shost, |
| enum iscsi_host_param param, char *buf) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| int len; |
| |
| switch (param) { |
| case ISCSI_HOST_PARAM_HWADDRESS: |
| len = sysfs_format_mac(buf, ha->my_mac, MAC_ADDR_LEN); |
| break; |
| case ISCSI_HOST_PARAM_IPADDRESS: |
| len = sprintf(buf, "%pI4\n", &ha->ip_config.ip_address); |
| break; |
| case ISCSI_HOST_PARAM_INITIATOR_NAME: |
| len = sprintf(buf, "%s\n", ha->name_string); |
| break; |
| case ISCSI_HOST_PARAM_PORT_STATE: |
| qla4xxx_set_port_state(shost); |
| len = sprintf(buf, "%s\n", iscsi_get_port_state_name(shost)); |
| break; |
| case ISCSI_HOST_PARAM_PORT_SPEED: |
| qla4xxx_set_port_speed(shost); |
| len = sprintf(buf, "%s\n", iscsi_get_port_speed_name(shost)); |
| break; |
| default: |
| return -ENOSYS; |
| } |
| |
| return len; |
| } |
| |
| static void qla4xxx_create_ipv4_iface(struct scsi_qla_host *ha) |
| { |
| if (ha->iface_ipv4) |
| return; |
| |
| /* IPv4 */ |
| ha->iface_ipv4 = iscsi_create_iface(ha->host, |
| &qla4xxx_iscsi_transport, |
| ISCSI_IFACE_TYPE_IPV4, 0, 0); |
| if (!ha->iface_ipv4) |
| ql4_printk(KERN_ERR, ha, "Could not create IPv4 iSCSI " |
| "iface0.\n"); |
| } |
| |
| static void qla4xxx_create_ipv6_iface(struct scsi_qla_host *ha) |
| { |
| if (!ha->iface_ipv6_0) |
| /* IPv6 iface-0 */ |
| ha->iface_ipv6_0 = iscsi_create_iface(ha->host, |
| &qla4xxx_iscsi_transport, |
| ISCSI_IFACE_TYPE_IPV6, 0, |
| 0); |
| if (!ha->iface_ipv6_0) |
| ql4_printk(KERN_ERR, ha, "Could not create IPv6 iSCSI " |
| "iface0.\n"); |
| |
| if (!ha->iface_ipv6_1) |
| /* IPv6 iface-1 */ |
| ha->iface_ipv6_1 = iscsi_create_iface(ha->host, |
| &qla4xxx_iscsi_transport, |
| ISCSI_IFACE_TYPE_IPV6, 1, |
| 0); |
| if (!ha->iface_ipv6_1) |
| ql4_printk(KERN_ERR, ha, "Could not create IPv6 iSCSI " |
| "iface1.\n"); |
| } |
| |
| static void qla4xxx_create_ifaces(struct scsi_qla_host *ha) |
| { |
| if (ha->ip_config.ipv4_options & IPOPT_IPV4_PROTOCOL_ENABLE) |
| qla4xxx_create_ipv4_iface(ha); |
| |
| if (ha->ip_config.ipv6_options & IPV6_OPT_IPV6_PROTOCOL_ENABLE) |
| qla4xxx_create_ipv6_iface(ha); |
| } |
| |
| static void qla4xxx_destroy_ipv4_iface(struct scsi_qla_host *ha) |
| { |
| if (ha->iface_ipv4) { |
| iscsi_destroy_iface(ha->iface_ipv4); |
| ha->iface_ipv4 = NULL; |
| } |
| } |
| |
| static void qla4xxx_destroy_ipv6_iface(struct scsi_qla_host *ha) |
| { |
| if (ha->iface_ipv6_0) { |
| iscsi_destroy_iface(ha->iface_ipv6_0); |
| ha->iface_ipv6_0 = NULL; |
| } |
| if (ha->iface_ipv6_1) { |
| iscsi_destroy_iface(ha->iface_ipv6_1); |
| ha->iface_ipv6_1 = NULL; |
| } |
| } |
| |
| static void qla4xxx_destroy_ifaces(struct scsi_qla_host *ha) |
| { |
| qla4xxx_destroy_ipv4_iface(ha); |
| qla4xxx_destroy_ipv6_iface(ha); |
| } |
| |
| static void qla4xxx_set_ipv6(struct scsi_qla_host *ha, |
| struct iscsi_iface_param_info *iface_param, |
| struct addr_ctrl_blk *init_fw_cb) |
| { |
| /* |
| * iface_num 0 is valid for IPv6 Addr, linklocal, router, autocfg. |
| * iface_num 1 is valid only for IPv6 Addr. |
| */ |
| switch (iface_param->param) { |
| case ISCSI_NET_PARAM_IPV6_ADDR: |
| if (iface_param->iface_num & 0x1) |
| /* IPv6 Addr 1 */ |
| memcpy(init_fw_cb->ipv6_addr1, iface_param->value, |
| sizeof(init_fw_cb->ipv6_addr1)); |
| else |
| /* IPv6 Addr 0 */ |
| memcpy(init_fw_cb->ipv6_addr0, iface_param->value, |
| sizeof(init_fw_cb->ipv6_addr0)); |
| break; |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL: |
| if (iface_param->iface_num & 0x1) |
| break; |
| memcpy(init_fw_cb->ipv6_if_id, &iface_param->value[8], |
| sizeof(init_fw_cb->ipv6_if_id)); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ROUTER: |
| if (iface_param->iface_num & 0x1) |
| break; |
| memcpy(init_fw_cb->ipv6_dflt_rtr_addr, iface_param->value, |
| sizeof(init_fw_cb->ipv6_dflt_rtr_addr)); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG: |
| /* Autocfg applies to even interface */ |
| if (iface_param->iface_num & 0x1) |
| break; |
| |
| if (iface_param->value[0] == ISCSI_IPV6_AUTOCFG_DISABLE) |
| init_fw_cb->ipv6_addtl_opts &= |
| cpu_to_le16( |
| ~IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE); |
| else if (iface_param->value[0] == ISCSI_IPV6_AUTOCFG_ND_ENABLE) |
| init_fw_cb->ipv6_addtl_opts |= |
| cpu_to_le16( |
| IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE); |
| else |
| ql4_printk(KERN_ERR, ha, |
| "Invalid autocfg setting for IPv6 addr\n"); |
| break; |
| case ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG: |
| /* Autocfg applies to even interface */ |
| if (iface_param->iface_num & 0x1) |
| break; |
| |
| if (iface_param->value[0] == |
| ISCSI_IPV6_LINKLOCAL_AUTOCFG_ENABLE) |
| init_fw_cb->ipv6_addtl_opts |= cpu_to_le16( |
| IPV6_ADDOPT_AUTOCONFIG_LINK_LOCAL_ADDR); |
| else if (iface_param->value[0] == |
| ISCSI_IPV6_LINKLOCAL_AUTOCFG_DISABLE) |
| init_fw_cb->ipv6_addtl_opts &= cpu_to_le16( |
| ~IPV6_ADDOPT_AUTOCONFIG_LINK_LOCAL_ADDR); |
| else |
| ql4_printk(KERN_ERR, ha, |
| "Invalid autocfg setting for IPv6 linklocal addr\n"); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG: |
| /* Autocfg applies to even interface */ |
| if (iface_param->iface_num & 0x1) |
| break; |
| |
| if (iface_param->value[0] == ISCSI_IPV6_ROUTER_AUTOCFG_ENABLE) |
| memset(init_fw_cb->ipv6_dflt_rtr_addr, 0, |
| sizeof(init_fw_cb->ipv6_dflt_rtr_addr)); |
| break; |
| case ISCSI_NET_PARAM_IFACE_ENABLE: |
| if (iface_param->value[0] == ISCSI_IFACE_ENABLE) { |
| init_fw_cb->ipv6_opts |= |
| cpu_to_le16(IPV6_OPT_IPV6_PROTOCOL_ENABLE); |
| qla4xxx_create_ipv6_iface(ha); |
| } else { |
| init_fw_cb->ipv6_opts &= |
| cpu_to_le16(~IPV6_OPT_IPV6_PROTOCOL_ENABLE & |
| 0xFFFF); |
| qla4xxx_destroy_ipv6_iface(ha); |
| } |
| break; |
| case ISCSI_NET_PARAM_VLAN_TAG: |
| if (iface_param->len != sizeof(init_fw_cb->ipv6_vlan_tag)) |
| break; |
| init_fw_cb->ipv6_vlan_tag = |
| cpu_to_be16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_VLAN_ENABLED: |
| if (iface_param->value[0] == ISCSI_VLAN_ENABLE) |
| init_fw_cb->ipv6_opts |= |
| cpu_to_le16(IPV6_OPT_VLAN_TAGGING_ENABLE); |
| else |
| init_fw_cb->ipv6_opts &= |
| cpu_to_le16(~IPV6_OPT_VLAN_TAGGING_ENABLE); |
| break; |
| case ISCSI_NET_PARAM_MTU: |
| init_fw_cb->eth_mtu_size = |
| cpu_to_le16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_PORT: |
| /* Autocfg applies to even interface */ |
| if (iface_param->iface_num & 0x1) |
| break; |
| |
| init_fw_cb->ipv6_port = |
| cpu_to_le16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_DELAYED_ACK_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_DISABLE) |
| init_fw_cb->ipv6_tcp_opts |= |
| cpu_to_le16(IPV6_TCPOPT_DELAYED_ACK_DISABLE); |
| else |
| init_fw_cb->ipv6_tcp_opts &= |
| cpu_to_le16(~IPV6_TCPOPT_DELAYED_ACK_DISABLE & |
| 0xFFFF); |
| break; |
| case ISCSI_NET_PARAM_TCP_NAGLE_DISABLE: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_DISABLE) |
| init_fw_cb->ipv6_tcp_opts |= |
| cpu_to_le16(IPV6_TCPOPT_NAGLE_ALGO_DISABLE); |
| else |
| init_fw_cb->ipv6_tcp_opts &= |
| cpu_to_le16(~IPV6_TCPOPT_NAGLE_ALGO_DISABLE); |
| break; |
| case ISCSI_NET_PARAM_TCP_WSF_DISABLE: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_DISABLE) |
| init_fw_cb->ipv6_tcp_opts |= |
| cpu_to_le16(IPV6_TCPOPT_WINDOW_SCALE_DISABLE); |
| else |
| init_fw_cb->ipv6_tcp_opts &= |
| cpu_to_le16(~IPV6_TCPOPT_WINDOW_SCALE_DISABLE); |
| break; |
| case ISCSI_NET_PARAM_TCP_WSF: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_tcp_wsf = iface_param->value[0]; |
| break; |
| case ISCSI_NET_PARAM_TCP_TIMER_SCALE: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_tcp_opts &= |
| cpu_to_le16(~IPV6_TCPOPT_TIMER_SCALE); |
| init_fw_cb->ipv6_tcp_opts |= |
| cpu_to_le16((iface_param->value[0] << 1) & |
| IPV6_TCPOPT_TIMER_SCALE); |
| break; |
| case ISCSI_NET_PARAM_TCP_TIMESTAMP_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv6_tcp_opts |= |
| cpu_to_le16(IPV6_TCPOPT_TIMESTAMP_EN); |
| else |
| init_fw_cb->ipv6_tcp_opts &= |
| cpu_to_le16(~IPV6_TCPOPT_TIMESTAMP_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv6_opts |= |
| cpu_to_le16(IPV6_OPT_GRAT_NEIGHBOR_ADV_EN); |
| else |
| init_fw_cb->ipv6_opts &= |
| cpu_to_le16(~IPV6_OPT_GRAT_NEIGHBOR_ADV_EN); |
| break; |
| case ISCSI_NET_PARAM_REDIRECT_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv6_opts |= |
| cpu_to_le16(IPV6_OPT_REDIRECT_EN); |
| else |
| init_fw_cb->ipv6_opts &= |
| cpu_to_le16(~IPV6_OPT_REDIRECT_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV6_MLD_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv6_addtl_opts |= |
| cpu_to_le16(IPV6_ADDOPT_MLD_EN); |
| else |
| init_fw_cb->ipv6_addtl_opts &= |
| cpu_to_le16(~IPV6_ADDOPT_MLD_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV6_FLOW_LABEL: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_flow_lbl = |
| cpu_to_le16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_traffic_class = iface_param->value[0]; |
| break; |
| case ISCSI_NET_PARAM_IPV6_HOP_LIMIT: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_hop_limit = iface_param->value[0]; |
| break; |
| case ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_nd_reach_time = |
| cpu_to_le32(*(uint32_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_nd_rexmit_timer = |
| cpu_to_le32(*(uint32_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_IPV6_ND_STALE_TMO: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_nd_stale_timeout = |
| cpu_to_le32(*(uint32_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_dup_addr_detect_count = iface_param->value[0]; |
| break; |
| case ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv6_gw_advrt_mtu = |
| cpu_to_le32(*(uint32_t *)iface_param->value); |
| break; |
| default: |
| ql4_printk(KERN_ERR, ha, "Unknown IPv6 param = %d\n", |
| iface_param->param); |
| break; |
| } |
| } |
| |
| static void qla4xxx_set_ipv4(struct scsi_qla_host *ha, |
| struct iscsi_iface_param_info *iface_param, |
| struct addr_ctrl_blk *init_fw_cb) |
| { |
| switch (iface_param->param) { |
| case ISCSI_NET_PARAM_IPV4_ADDR: |
| memcpy(init_fw_cb->ipv4_addr, iface_param->value, |
| sizeof(init_fw_cb->ipv4_addr)); |
| break; |
| case ISCSI_NET_PARAM_IPV4_SUBNET: |
| memcpy(init_fw_cb->ipv4_subnet, iface_param->value, |
| sizeof(init_fw_cb->ipv4_subnet)); |
| break; |
| case ISCSI_NET_PARAM_IPV4_GW: |
| memcpy(init_fw_cb->ipv4_gw_addr, iface_param->value, |
| sizeof(init_fw_cb->ipv4_gw_addr)); |
| break; |
| case ISCSI_NET_PARAM_IPV4_BOOTPROTO: |
| if (iface_param->value[0] == ISCSI_BOOTPROTO_DHCP) |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16(TCPOPT_DHCP_ENABLE); |
| else if (iface_param->value[0] == ISCSI_BOOTPROTO_STATIC) |
| init_fw_cb->ipv4_tcp_opts &= |
| cpu_to_le16(~TCPOPT_DHCP_ENABLE); |
| else |
| ql4_printk(KERN_ERR, ha, "Invalid IPv4 bootproto\n"); |
| break; |
| case ISCSI_NET_PARAM_IFACE_ENABLE: |
| if (iface_param->value[0] == ISCSI_IFACE_ENABLE) { |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_IPV4_PROTOCOL_ENABLE); |
| qla4xxx_create_ipv4_iface(ha); |
| } else { |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_IPV4_PROTOCOL_ENABLE & |
| 0xFFFF); |
| qla4xxx_destroy_ipv4_iface(ha); |
| } |
| break; |
| case ISCSI_NET_PARAM_VLAN_TAG: |
| if (iface_param->len != sizeof(init_fw_cb->ipv4_vlan_tag)) |
| break; |
| init_fw_cb->ipv4_vlan_tag = |
| cpu_to_be16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_VLAN_ENABLED: |
| if (iface_param->value[0] == ISCSI_VLAN_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_VLAN_TAGGING_ENABLE); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_VLAN_TAGGING_ENABLE); |
| break; |
| case ISCSI_NET_PARAM_MTU: |
| init_fw_cb->eth_mtu_size = |
| cpu_to_le16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_PORT: |
| init_fw_cb->ipv4_port = |
| cpu_to_le16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_NET_PARAM_DELAYED_ACK_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_DISABLE) |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16(TCPOPT_DELAYED_ACK_DISABLE); |
| else |
| init_fw_cb->ipv4_tcp_opts &= |
| cpu_to_le16(~TCPOPT_DELAYED_ACK_DISABLE & |
| 0xFFFF); |
| break; |
| case ISCSI_NET_PARAM_TCP_NAGLE_DISABLE: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_DISABLE) |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16(TCPOPT_NAGLE_ALGO_DISABLE); |
| else |
| init_fw_cb->ipv4_tcp_opts &= |
| cpu_to_le16(~TCPOPT_NAGLE_ALGO_DISABLE); |
| break; |
| case ISCSI_NET_PARAM_TCP_WSF_DISABLE: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_DISABLE) |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16(TCPOPT_WINDOW_SCALE_DISABLE); |
| else |
| init_fw_cb->ipv4_tcp_opts &= |
| cpu_to_le16(~TCPOPT_WINDOW_SCALE_DISABLE); |
| break; |
| case ISCSI_NET_PARAM_TCP_WSF: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv4_tcp_wsf = iface_param->value[0]; |
| break; |
| case ISCSI_NET_PARAM_TCP_TIMER_SCALE: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv4_tcp_opts &= cpu_to_le16(~TCPOPT_TIMER_SCALE); |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16((iface_param->value[0] << 1) & |
| TCPOPT_TIMER_SCALE); |
| break; |
| case ISCSI_NET_PARAM_TCP_TIMESTAMP_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16(TCPOPT_TIMESTAMP_ENABLE); |
| else |
| init_fw_cb->ipv4_tcp_opts &= |
| cpu_to_le16(~TCPOPT_TIMESTAMP_ENABLE); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16(TCPOPT_DNS_SERVER_IP_EN); |
| else |
| init_fw_cb->ipv4_tcp_opts &= |
| cpu_to_le16(~TCPOPT_DNS_SERVER_IP_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_tcp_opts |= |
| cpu_to_le16(TCPOPT_SLP_DA_INFO_EN); |
| else |
| init_fw_cb->ipv4_tcp_opts &= |
| cpu_to_le16(~TCPOPT_SLP_DA_INFO_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_TOS_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_IPV4_TOS_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_IPV4_TOS_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_TOS: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv4_tos = iface_param->value[0]; |
| break; |
| case ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_GRAT_ARP_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_GRAT_ARP_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_ALT_CID_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_ALT_CID_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID: |
| if (iface_param->iface_num & 0x1) |
| break; |
| memcpy(init_fw_cb->ipv4_dhcp_alt_cid, iface_param->value, |
| (sizeof(init_fw_cb->ipv4_dhcp_alt_cid) - 1)); |
| init_fw_cb->ipv4_dhcp_alt_cid_len = |
| strlen(init_fw_cb->ipv4_dhcp_alt_cid); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_REQ_VID_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_REQ_VID_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_USE_VID_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_USE_VID_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID: |
| if (iface_param->iface_num & 0x1) |
| break; |
| memcpy(init_fw_cb->ipv4_dhcp_vid, iface_param->value, |
| (sizeof(init_fw_cb->ipv4_dhcp_vid) - 1)); |
| init_fw_cb->ipv4_dhcp_vid_len = |
| strlen(init_fw_cb->ipv4_dhcp_vid); |
| break; |
| case ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_LEARN_IQN_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_LEARN_IQN_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_DISABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_FRAGMENTATION_DISABLE); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_FRAGMENTATION_DISABLE); |
| break; |
| case ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_IN_FORWARD_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_IN_FORWARD_EN); |
| break; |
| case ISCSI_NET_PARAM_REDIRECT_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->ipv4_ip_opts |= |
| cpu_to_le16(IPOPT_ARP_REDIRECT_EN); |
| else |
| init_fw_cb->ipv4_ip_opts &= |
| cpu_to_le16(~IPOPT_ARP_REDIRECT_EN); |
| break; |
| case ISCSI_NET_PARAM_IPV4_TTL: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->ipv4_ttl = iface_param->value[0]; |
| break; |
| default: |
| ql4_printk(KERN_ERR, ha, "Unknown IPv4 param = %d\n", |
| iface_param->param); |
| break; |
| } |
| } |
| |
| static void qla4xxx_set_iscsi_param(struct scsi_qla_host *ha, |
| struct iscsi_iface_param_info *iface_param, |
| struct addr_ctrl_blk *init_fw_cb) |
| { |
| switch (iface_param->param) { |
| case ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->def_timeout = |
| cpu_to_le16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_IFACE_PARAM_HDRDGST_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_HEADER_DIGEST_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_HEADER_DIGEST_EN); |
| break; |
| case ISCSI_IFACE_PARAM_DATADGST_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_DATA_DIGEST_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_DATA_DIGEST_EN); |
| break; |
| case ISCSI_IFACE_PARAM_IMM_DATA_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_IMMEDIATE_DATA_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_IMMEDIATE_DATA_EN); |
| break; |
| case ISCSI_IFACE_PARAM_INITIAL_R2T_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_INITIAL_R2T_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_INITIAL_R2T_EN); |
| break; |
| case ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_DATA_SEQ_INORDER_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_DATA_SEQ_INORDER_EN); |
| break; |
| case ISCSI_IFACE_PARAM_PDU_INORDER_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_DATA_PDU_INORDER_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_DATA_PDU_INORDER_EN); |
| break; |
| case ISCSI_IFACE_PARAM_ERL: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->iscsi_opts &= cpu_to_le16(~ISCSIOPTS_ERL); |
| init_fw_cb->iscsi_opts |= cpu_to_le16(iface_param->value[0] & |
| ISCSIOPTS_ERL); |
| break; |
| case ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->iscsi_max_pdu_size = |
| cpu_to_le32(*(uint32_t *)iface_param->value) / |
| BYTE_UNITS; |
| break; |
| case ISCSI_IFACE_PARAM_FIRST_BURST: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->iscsi_fburst_len = |
| cpu_to_le32(*(uint32_t *)iface_param->value) / |
| BYTE_UNITS; |
| break; |
| case ISCSI_IFACE_PARAM_MAX_R2T: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->iscsi_max_outstnd_r2t = |
| cpu_to_le16(*(uint16_t *)iface_param->value); |
| break; |
| case ISCSI_IFACE_PARAM_MAX_BURST: |
| if (iface_param->iface_num & 0x1) |
| break; |
| init_fw_cb->iscsi_max_burst_len = |
| cpu_to_le32(*(uint32_t *)iface_param->value) / |
| BYTE_UNITS; |
| break; |
| case ISCSI_IFACE_PARAM_CHAP_AUTH_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_CHAP_AUTH_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_CHAP_AUTH_EN); |
| break; |
| case ISCSI_IFACE_PARAM_BIDI_CHAP_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_BIDI_CHAP_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_BIDI_CHAP_EN); |
| break; |
| case ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_DISCOVERY_AUTH_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_DISCOVERY_AUTH_EN); |
| break; |
| case ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_DISCOVERY_LOGOUT_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_DISCOVERY_LOGOUT_EN); |
| break; |
| case ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN: |
| if (iface_param->iface_num & 0x1) |
| break; |
| if (iface_param->value[0] == ISCSI_NET_PARAM_ENABLE) |
| init_fw_cb->iscsi_opts |= |
| cpu_to_le16(ISCSIOPTS_STRICT_LOGIN_COMP_EN); |
| else |
| init_fw_cb->iscsi_opts &= |
| cpu_to_le16(~ISCSIOPTS_STRICT_LOGIN_COMP_EN); |
| break; |
| default: |
| ql4_printk(KERN_ERR, ha, "Unknown iscsi param = %d\n", |
| iface_param->param); |
| break; |
| } |
| } |
| |
| static void |
| qla4xxx_initcb_to_acb(struct addr_ctrl_blk *init_fw_cb) |
| { |
| struct addr_ctrl_blk_def *acb; |
| acb = (struct addr_ctrl_blk_def *)init_fw_cb; |
| memset(acb->reserved1, 0, sizeof(acb->reserved1)); |
| memset(acb->reserved2, 0, sizeof(acb->reserved2)); |
| memset(acb->reserved3, 0, sizeof(acb->reserved3)); |
| memset(acb->reserved4, 0, sizeof(acb->reserved4)); |
| memset(acb->reserved5, 0, sizeof(acb->reserved5)); |
| memset(acb->reserved6, 0, sizeof(acb->reserved6)); |
| memset(acb->reserved7, 0, sizeof(acb->reserved7)); |
| memset(acb->reserved8, 0, sizeof(acb->reserved8)); |
| memset(acb->reserved9, 0, sizeof(acb->reserved9)); |
| memset(acb->reserved10, 0, sizeof(acb->reserved10)); |
| memset(acb->reserved11, 0, sizeof(acb->reserved11)); |
| memset(acb->reserved12, 0, sizeof(acb->reserved12)); |
| memset(acb->reserved13, 0, sizeof(acb->reserved13)); |
| memset(acb->reserved14, 0, sizeof(acb->reserved14)); |
| memset(acb->reserved15, 0, sizeof(acb->reserved15)); |
| } |
| |
| static int |
| qla4xxx_iface_set_param(struct Scsi_Host *shost, void *data, uint32_t len) |
| { |
| struct scsi_qla_host *ha = to_qla_host(shost); |
| int rval = 0; |
| struct iscsi_iface_param_info *iface_param = NULL; |
| struct addr_ctrl_blk *init_fw_cb = NULL; |
| dma_addr_t init_fw_cb_dma; |
| uint32_t mbox_cmd[MBOX_REG_COUNT]; |
| uint32_t mbox_sts[MBOX_REG_COUNT]; |
| uint32_t rem = len; |
| struct nlattr *attr; |
| |
| init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, |
| sizeof(struct addr_ctrl_blk), |
| &init_fw_cb_dma, GFP_KERNEL); |
| if (!init_fw_cb) { |
| ql4_printk(KERN_ERR, ha, "%s: Unable to alloc init_cb\n", |
| __func__); |
| return -ENOMEM; |
| } |
| |
| memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); |
| memset(&mbox_cmd, 0, sizeof(mbox_cmd)); |
| memset(&mbox_sts, 0, sizeof(mbox_sts)); |
| |
| if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma)) { |
| ql4_printk(KERN_ERR, ha, "%s: get ifcb failed\n", __func__); |
| rval = -EIO; |
| goto exit_init_fw_cb; |
| } |
| |
| nla_for_each_attr(attr, data, len, rem) { |
| iface_param = nla_data(attr); |
| |
| if (iface_param->param_type == ISCSI_NET_PARAM) { |
| switch (iface_param->iface_type) { |
| case ISCSI_IFACE_TYPE_IPV4: |
| switch (iface_param->iface_num) { |
| case 0: |
| qla4xxx_set_ipv4(ha, iface_param, |
| init_fw_cb); |
| break; |
| default: |
| /* Cannot have more than one IPv4 interface */ |
| ql4_printk(KERN_ERR, ha, |
| "Invalid IPv4 iface number = %d\n", |
| iface_param->iface_num); |
| break; |
| } |
| break; |
| case ISCSI_IFACE_TYPE_IPV6: |
| switch (iface_param->iface_num) { |
| case 0: |
| case 1: |
| qla4xxx_set_ipv6(ha, iface_param, |
| init_fw_cb); |
| break; |
| default: |
| /* Cannot have more than two IPv6 interface */ |
| ql4_printk(KERN_ERR, ha, |
| "Invalid IPv6 iface number = %d\n", |
| iface_param->iface_num); |
| break; |
| } |
| break; |
| default: |
| ql4_printk(KERN_ERR, ha, |
| "Invalid iface type\n"); |
| break; |
| } |
| } else if (iface_param->param_type == ISCSI_IFACE_PARAM) { |
| qla4xxx_set_iscsi_param(ha, iface_param, |
| init_fw_cb); |
| } else { |
| continue; |
| } |
| } |
| |
| init_fw_cb->cookie = cpu_to_le32(0x11BEAD5A); |
| |
| rval = qla4xxx_set_flash(ha, init_fw_cb_dma, FLASH_SEGMENT_IFCB, |
| sizeof(struct addr_ctrl_blk), |
| FLASH_OPT_RMW_COMMIT); |
| if (rval != QLA_SUCCESS) { |
| ql4_printk(KERN_ERR, ha, "%s: set flash mbx failed\n", |
| __func__); |
| rval = -EIO; |
| goto exit_init_fw_cb; |
| } |
| |
| rval = qla4xxx_disable_acb(ha); |
| if (rval != QLA_SUCCESS) { |
| ql4_printk(KERN_ERR, ha, "%s: disable acb mbx failed\n", |
| __func__); |
| rval = -EIO; |
| goto exit_init_fw_cb; |
| } |
| |
| wait_for_completion_timeout(&ha->disable_acb_comp, |
| DISABLE_ACB_TOV * HZ); |
| |
| qla4xxx_initcb_to_acb(init_fw_cb); |
| |
| rval = qla4xxx_set_acb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma); |
| if (rval != QLA_SUCCESS) { |
| ql4_printk(KERN_ERR, ha, "%s: set acb mbx failed\n", |
| __func__); |
| rval = -EIO; |
| goto exit_init_fw_cb; |
| } |
| |
| memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); |
| qla4xxx_update_local_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb, |
| init_fw_cb_dma); |
| |
| exit_init_fw_cb: |
| dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk), |
| init_fw_cb, init_fw_cb_dma); |
| |
| return rval; |
| } |
| |
| static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess, |
| enum iscsi_param param, char *buf) |
| { |
| struct iscsi_session *sess = cls_sess->dd_data; |
| struct ddb_entry *ddb_entry = sess->dd_data; |
| struct scsi_qla_host *ha = ddb_entry->ha; |
| struct iscsi_cls_conn *cls_conn = ddb_entry->conn; |
| struct ql4_chap_table chap_tbl; |
| int rval, len; |
| uint16_t idx; |
| |
| memset(&chap_tbl, 0, sizeof(chap_tbl)); |
| switch (param) { |
| case ISCSI_PARAM_CHAP_IN_IDX: |
| rval = qla4xxx_get_chap_index(ha, sess->username_in, |
| sess->password_in, BIDI_CHAP, |
| &idx); |
| if (rval) |
| len = sprintf(buf, "\n"); |
| else |
| len = sprintf(buf, "%hu\n", idx); |
| break; |
| case ISCSI_PARAM_CHAP_OUT_IDX: |
| if (ddb_entry->ddb_type == FLASH_DDB) { |
| if (ddb_entry->chap_tbl_idx != INVALID_ENTRY) { |
| idx = ddb_entry->chap_tbl_idx; |
| rval = QLA_SUCCESS; |
| } else { |
| rval = QLA_ERROR; |
| } |
| } else { |
| rval = qla4xxx_get_chap_index(ha, sess->username, |
| sess->password, |
| LOCAL_CHAP, &idx); |
| } |
| if (rval) |
| len = sprintf(buf, "\n"); |
| else |
| len = sprintf(buf, "%hu\n", idx); |
| break; |
| case ISCSI_PARAM_USERNAME: |
| case ISCSI_PARAM_PASSWORD: |
| /* First, populate session username and password for FLASH DDB, |
| * if not already done. This happens when session login fails |
| * for a FLASH DDB. |
| */ |
| if (ddb_entry->ddb_type == FLASH_DDB && |
| ddb_entry->chap_tbl_idx != INVALID_ENTRY && |
| !sess->username && !sess->password) { |
| idx = ddb_entry->chap_tbl_idx; |
| rval = qla4xxx_get_uni_chap_at_index(ha, chap_tbl.name, |
| chap_tbl.secret, |
| idx); |
| if (!rval) { |
| iscsi_set_param(cls_conn, ISCSI_PARAM_USERNAME, |
| (char *)chap_tbl.name, |
| strlen((char *)chap_tbl.name)); |
| iscsi_set_param(cls_conn, ISCSI_PARAM_PASSWORD, |
| (char *)chap_tbl.secret, |
| chap_tbl.secret_len); |
| } |
| } |
| /* allow fall-through */ |
| default: |
| return iscsi_session_get_param(cls_sess, param, buf); |
| } |
| |
| return len; |
| } |
| |
| static int qla4xxx_conn_get_param(struct iscsi_cls_conn *cls_conn, |
| enum iscsi_param param, char *buf) |
| { |
| struct iscsi_conn *conn; |
| struct qla_conn *qla_conn; |
| struct sockaddr *dst_addr; |
| |
| conn = cls_conn->dd_data; |
| qla_conn = conn->dd_data; |
| dst_addr = (struct sockaddr *)&qla_conn->qla_ep->dst_addr; |
| |
| switch (param) { |
| case ISCSI_PARAM_CONN_PORT: |
| case ISCSI_PARAM_CONN_ADDRESS: |
| return iscsi_conn_get_addr_param((struct sockaddr_storage *) |
| dst_addr, param, buf); |
| default: |
| return iscsi_conn_get_param(cls_conn, param, buf); |
| } |
| } |
| |
| int qla4xxx_get_ddb_index(struct scsi_qla_host *ha, uint16_t *ddb_index) |
| { |
| uint32_t mbx_sts = 0; |
| uint16_t tmp_ddb_index; |
| int ret; |
| |
| get_ddb_index: |
| tmp_ddb_index = find_first_zero_bit(ha->ddb_idx_map, MAX_DDB_ENTRIES); |
| |
| if (tmp_ddb_index >= MAX_DDB_ENTRIES) { |
| DEBUG2(ql4_printk(KERN_INFO, ha, |
| "Free DDB index not available\n")); |
| ret = QLA_ERROR; |
| goto exit_get_ddb_index; |
| } |
| |
| if (test_and_set_bit(tmp_ddb_index, ha->ddb_idx_map)) |
| goto get_ddb_index; |
| |
| DEBUG2(ql4_printk(KERN_INFO, ha, |
| "Found a free DDB index at %d\n", tmp_ddb_index)); |
| ret = qla4xxx_req_ddb_entry(ha, tmp_ddb_index, &mbx_sts); |
| if (ret == QLA_ERROR) { |
| if (mbx_sts == MBOX_STS_COMMAND_ERROR) { |
| ql4_printk(KERN_INFO, ha, |
| "DDB index = %d not available trying next\n", |
| tmp_ddb_index); |
| goto get_ddb_index; |
| } |
| DEBUG2(ql4_printk(KERN_INFO, ha, |
| "Free FW DDB not available\n")); |
| } |
| |
| *ddb_index = tmp_ddb_index; |
| |
| exit_get_ddb_index: |
| return ret; |
| } |
| |
| static int qla4xxx_match_ipaddress(struct scsi_qla_host *ha, |
| struct ddb_entry *ddb_entry, |
| char *existing_ipaddr, |
| char *user_ipaddr) |
| { |
| uint8_t dst_ipaddr[IPv6_ADDR_LEN]; |
| char formatted_ipaddr[DDB_IPADDR_LEN]; |
| int status = QLA_SUCCESS, ret = 0; |
| |
| if (ddb_entry->fw_ddb_entry.options & DDB_OPT_IPV6_DEVICE) { |
| ret = in6_pton(user_ipaddr, strlen(user_ipaddr), dst_ipaddr, |
| '\0', NULL); |
| if (ret == 0) { |
| status = QLA_ERROR; |
| goto out_match; |
| } |
| ret = sprintf(formatted_ipaddr, "%pI6", dst_ipaddr); |
| } else { |
| ret = in4_pton(user_ipaddr, strlen(user_ipaddr), dst_ipaddr, |
| '\0', NULL); |
| if (ret == 0) { |
| status = QLA_ERROR; |
| goto out_match; |
| } |
| ret = sprintf(formatted_ipaddr, "%pI4", dst_ipaddr |