Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1030389AbVIITn2 (ORCPT ); Fri, 9 Sep 2005 15:43:28 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1030369AbVIITm6 (ORCPT ); Fri, 9 Sep 2005 15:42:58 -0400 Received: from magic.adaptec.com ([216.52.22.17]:37063 "EHLO magic.adaptec.com") by vger.kernel.org with ESMTP id S1030398AbVIITmR (ORCPT ); Fri, 9 Sep 2005 15:42:17 -0400 Message-ID: <4321E593.5090803@adaptec.com> Date: Fri, 09 Sep 2005 15:42:11 -0400 From: Luben Tuikov User-Agent: Mozilla Thunderbird 1.0.6 (X11/20050716) X-Accept-Language: en-us, en MIME-Version: 1.0 To: Linux Kernel Mailing List , SCSI Mailing List Subject: [PATCH 2.6.13 13/14] sas-class: sas_port.c SAS Port (events, attrs, initialization) Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-OriginalArrivalTime: 09 Sep 2005 19:42:16.0510 (UTC) FILETIME=[95462DE0:01C5B576] Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 12844 Lines: 443 Signed-off-by: Luben Tuikov diff -X linux-2.6.13/Documentation/dontdiff -Naur linux-2.6.13-orig/drivers/scsi/sas-class/sas_port.c linux-2.6.13/drivers/scsi/sas-class/sas_port.c --- linux-2.6.13-orig/drivers/scsi/sas-class/sas_port.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.13/drivers/scsi/sas-class/sas_port.c 2005-09-09 11:50:35.000000000 -0400 @@ -0,0 +1,430 @@ +/* + * Serial Attached SCSI (SAS) Port class + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: //depot/sas-class/sas_port.c#41 $ + */ + +#include "sas_internal.h" +#include + +/* called only when num_phys increments, afterwards */ +static void sas_create_port_sysfs_links(struct sas_phy *phy) +{ + struct sas_port *port = phy->port; + + if (port->num_phys == 1) { + kobject_register(&port->port_kobj); + kset_register(&port->phy_kset); + kset_register(&port->dev_kset); + } + /* add port->phy link */ + sysfs_create_link(&port->phy_kset.kobj, &phy->phy_kobj, + kobject_name(&phy->phy_kobj)); + /* add phy->port link */ + sysfs_create_link(&phy->phy_kobj, &port->port_kobj, "port"); +} + +/* called only when num_phys decrements, just before it does */ +static void sas_remove_port_sysfs_links(struct sas_phy *phy) +{ + struct sas_port *port = phy->port; + + /* remove phy->port link */ + sysfs_remove_link(&phy->phy_kobj, "port"); + /* remove port to phy link */ + sysfs_remove_link(&port->phy_kset.kobj, kobject_name(&phy->phy_kobj)); + + if (port->num_phys == 1) { + kset_unregister(&port->dev_kset); + kset_unregister(&port->phy_kset); + kobject_unregister(&port->port_kobj); + } +} + +/** + * sas_form_port -- add this phy to a port + * @phy: the phy of interest + * + * This function adds this phy to an existing port, thus creating a wide + * port, or it creates a port and adds the phy to the port. + */ +static void sas_form_port(struct sas_phy *phy) +{ + int i; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_port *port = phy->port; + + if (port) { + if (memcmp(port->attached_sas_addr, phy->attached_sas_addr, + SAS_ADDR_SIZE) == 0) + sas_deform_port(phy); + else { + SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", + __FUNCTION__, phy->id, phy->port->id, + phy->port->num_phys); + return; + } + } + + /* find a port */ + spin_lock(&sas_ha->phy_port_lock); + for (i = 0; i < sas_ha->num_phys; i++) { + port = sas_ha->sas_port[i]; + spin_lock(&port->phy_list_lock); + if (*(u64 *) port->sas_addr && + memcmp(port->attached_sas_addr, + phy->attached_sas_addr, SAS_ADDR_SIZE) == 0 && + port->num_phys > 0) { + /* wide port */ + SAS_DPRINTK("phy%d matched wide port%d\n", phy->id, + port->id); + break; + } else if (*(u64 *) port->sas_addr == 0 && port->num_phys==0) { + memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE); + break; + } + spin_unlock(&port->phy_list_lock); + } + + if (i >= sas_ha->num_phys) { + printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n", + __FUNCTION__); + spin_unlock(&sas_ha->phy_port_lock); + return; + } + + /* add the phy to the port */ + list_add_tail(&phy->port_phy_el, &port->phy_list); + phy->port = port; + port->num_phys++; + port->phy_mask |= (1U << phy->id); + + SAS_DPRINTK("phy%d added to port%d, phy_mask:0x%x\n", phy->id, + port->id, port->phy_mask); + + if (*(u64 *)port->attached_sas_addr == 0) { + port->class = phy->class; + memcpy(port->attached_sas_addr, phy->attached_sas_addr, + SAS_ADDR_SIZE); + port->iproto = phy->iproto; + port->tproto = phy->tproto; + port->oob_mode = phy->oob_mode; + port->linkrate = phy->linkrate; + } else + port->linkrate = max(port->linkrate, phy->linkrate); + spin_unlock(&port->phy_list_lock); + spin_unlock(&sas_ha->phy_port_lock); + + if (port->port_dev) + port->port_dev->pathways = port->num_phys; + + sas_create_port_sysfs_links(phy); + /* Tell the LLDD about this port formation. */ + if (sas_ha->lldd_port_formed) + sas_ha->lldd_port_formed(phy); + + sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN); +} + +/** + * sas_deform_port -- remove this phy from the port it belongs to + * @phy: the phy of interest + * + * This is called when the physical link to the other phy has been + * lost (on this phy), in Event thread context. We cannot delay here. + */ +void sas_deform_port(struct sas_phy *phy) +{ + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_port *port = phy->port; + + if (!port) + return; /* done by a phy event */ + + if (port->port_dev) + port->port_dev->pathways--; + + if (port->num_phys == 1) { + init_completion(&port->port_gone_completion); + sas_discover_event(port, DISCE_PORT_GONE); + wait_for_completion(&port->port_gone_completion); + } + + if (sas_ha->lldd_port_deformed) + sas_ha->lldd_port_deformed(phy); + + sas_remove_port_sysfs_links(phy); + + spin_lock(&sas_ha->phy_port_lock); + spin_lock(&port->phy_list_lock); + + list_del_init(&phy->port_phy_el); + phy->port = NULL; + port->num_phys--; + port->phy_mask &= ~(1U << phy->id); + + if (port->num_phys == 0) { + INIT_LIST_HEAD(&port->phy_list); + memset(port->sas_addr, 0, SAS_ADDR_SIZE); + memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE); + port->class = 0; + port->iproto = 0; + port->tproto = 0; + port->oob_mode = 0; + port->phy_mask = 0; + } + spin_unlock(&port->phy_list_lock); + spin_unlock(&sas_ha->phy_port_lock); + + return; +} + +/* ---------- SAS port events ---------- */ + +void sas_porte_bytes_dmaed(struct sas_phy *phy) +{ + sas_form_port(phy); +} + +void sas_porte_broadcast_rcvd(struct sas_phy *phy) +{ + unsigned long flags; + u32 prim; + + spin_lock_irqsave(&phy->sas_prim_lock, flags); + prim = phy->sas_prim; + spin_unlock_irqrestore(&phy->sas_prim_lock, flags); + + SAS_DPRINTK("broadcast received: %d\n", prim); + sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN); +} + +void sas_porte_link_reset_err(struct sas_phy *phy) +{ + sas_deform_port(phy); +} + +void sas_porte_timer_event(struct sas_phy *phy) +{ + sas_deform_port(phy); +} + +void sas_porte_hard_reset(struct sas_phy *phy) +{ + sas_deform_port(phy); +} + +/* ---------- SAS port attributes ---------- */ + +static ssize_t sas_port_id_show(struct sas_port *port, char *buf) +{ + return sprintf(buf, "%d\n", port->id); +} + +static ssize_t sas_port_class_show(struct sas_port *port, char *buf) +{ + return sas_show_class(port->class, buf); +} + +static ssize_t sas_port_sas_addr_show(struct sas_port *port, char *buf) +{ + return sprintf(buf, "%llx\n", SAS_ADDR(port->sas_addr)); +} + +static ssize_t sas_port_attached_sas_addr_show(struct sas_port *port,char *buf) +{ + return sprintf(buf, "%llx\n", SAS_ADDR(port->attached_sas_addr)); +} + +static ssize_t sas_port_iproto_show(struct sas_port *port, char *buf) +{ + return sas_show_proto(port->iproto, buf); +} + +static ssize_t sas_port_tproto_show(struct sas_port *port, char *buf) +{ + return sas_show_proto(port->tproto, buf); +} + +static ssize_t sas_port_oob_mode_show(struct sas_port *port, char *buf) +{ + return sas_show_oob_mode(port->oob_mode, buf); +} + +struct port_attribute { + struct attribute attr; + ssize_t (*show)(struct sas_port *port, char *); + ssize_t (*store)(struct sas_port *port, const char *, size_t); +}; + +static struct port_attribute port_attrs[] = { + __ATTR(id, 0444, sas_port_id_show, NULL), + __ATTR(class, 0444, sas_port_class_show, NULL), + __ATTR(port_identifier, 0444, sas_port_sas_addr_show, NULL), + __ATTR(attached_port_identifier, 0444, sas_port_attached_sas_addr_show, NULL), + __ATTR(iproto, 0444, sas_port_iproto_show, NULL), + __ATTR(tproto, 0444, sas_port_tproto_show, NULL), + __ATTR(oob_mode, 0444, sas_port_oob_mode_show, NULL), + __ATTR_NULL, +}; + +static struct attribute *def_attrs[ARRAY_SIZE(port_attrs)]; + +#define to_sas_port(_obj) container_of(_obj, struct sas_port, port_kobj) +#define to_port_attr(_attr) container_of(_attr, struct port_attribute, attr) + +static ssize_t port_show_attr(struct kobject *kobj, struct attribute *attr, + char *page) +{ + ssize_t ret = 0; + struct sas_port *port = to_sas_port(kobj); + struct port_attribute *port_attr = to_port_attr(attr); + + if (port_attr->show) + ret = port_attr->show(port, page); + return ret; +} + +static struct sysfs_ops port_sysfs_ops = { + .show = port_show_attr, +}; + +static struct kobj_type port_type = { + .sysfs_ops = &port_sysfs_ops, + .default_attrs = def_attrs, +}; + +/* ---------- SAS port registration ---------- */ + +static void sas_init_port(struct sas_port *port, + struct sas_ha_struct *sas_ha, int i, + struct kset *parent_kset) +{ + port->id = i; + INIT_LIST_HEAD(&port->dev_list); + spin_lock_init(&port->phy_list_lock); + INIT_LIST_HEAD(&port->phy_list); + port->num_phys = 0; + port->phy_mask = 0; + port->ha = sas_ha; + + memset(&port->port_kobj, 0, sizeof(port->port_kobj)); + memset(&port->phy_kset, 0, sizeof(port->phy_kset)); + memset(&port->dev_kset, 0, sizeof(port->dev_kset)); + + kobject_set_name(&port->port_kobj, "%d", port->id); + port->port_kobj.kset = parent_kset; + port->port_kobj.ktype= parent_kset->ktype; + + kobject_set_name(&port->phy_kset.kobj, "%s", "phys"); + port->phy_kset.kobj.parent = &port->port_kobj; + port->phy_kset.ktype = NULL; + + kobject_set_name(&port->dev_kset.kobj, "%s", "domain"); + port->dev_kset.kobj.parent = &port->port_kobj; + port->dev_kset.ktype = NULL; + + port->id_map.max_ids = 128; + port->id_map.id_bitmap_size = + BITS_TO_LONGS(port->id_map.max_ids)*sizeof(long); + port->id_map.id_bitmap = kmalloc(port->id_map.id_bitmap_size, + GFP_KERNEL); + memset(port->id_map.id_bitmap, 0, port->id_map.id_bitmap_size); + spin_lock_init(&port->id_map.id_bitmap_lock); +} + +int sas_register_ports(struct sas_ha_struct *sas_ha) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(def_attrs)-1; i++) + def_attrs[i] = &port_attrs[i].attr; + def_attrs[i] = NULL; + + /* make sas/ha/ports/ appear */ + kobject_set_name(&sas_ha->port_kset.kobj, "%s", "ports"); + sas_ha->port_kset.kobj.kset = &sas_ha->ha_kset; /* parent */ + /* no type inheritance */ + sas_ha->port_kset.kobj.ktype = NULL; + sas_ha->port_kset.ktype = &port_type; /* children are of this type */ + + /* initialize the ports and discovery */ + for (i = 0; i < sas_ha->num_phys; i++) { + struct sas_port *port = sas_ha->sas_port[i]; + + sas_init_port(port, sas_ha, i, &sas_ha->port_kset); + sas_init_disc(&port->disc, port); + } + + return kset_register(&sas_ha->port_kset); +} + +void sas_unregister_ports(struct sas_ha_struct *sas_ha) +{ + int i; + + for (i = 0; i < sas_ha->num_phys; i++) + if (sas_ha->sas_phy[i]->port) + sas_deform_port(sas_ha->sas_phy[i]); + + for (i = 0; i < sas_ha->num_phys; i++) { + kfree(sas_ha->sas_port[i]->id_map.id_bitmap); + sas_ha->sas_port[i]->id_map.id_bitmap = NULL; + } + + kset_unregister(&sas_ha->port_kset); +} + +int sas_reserve_free_id(struct sas_port *port) +{ + int id; + + spin_lock(&port->id_map.id_bitmap_lock); + id = find_first_zero_bit(port->id_map.id_bitmap, port->id_map.max_ids); + if (id >= port->id_map.max_ids) { + id = -ENOMEM; + spin_unlock(&port->id_map.id_bitmap_lock); + goto out; + } + set_bit(id, port->id_map.id_bitmap); + spin_unlock(&port->id_map.id_bitmap_lock); +out: + return id; +} + +void sas_reserve_scsi_id(struct sas_port *port, int id) +{ + if (0 > id || id >= port->id_map.max_ids) + return; + spin_lock(&port->id_map.id_bitmap_lock); + set_bit(id, port->id_map.id_bitmap); + spin_unlock(&port->id_map.id_bitmap_lock); +} + +void sas_release_scsi_id(struct sas_port *port, int id) +{ + if (0 > id || id >= port->id_map.max_ids) + return; + spin_lock(&port->id_map.id_bitmap_lock); + clear_bit(id, port->id_map.id_bitmap); + spin_unlock(&port->id_map.id_bitmap_lock); +} - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/