linux/drivers/s390/scsi/zfcp_unit.c
<<
>>
Prefs
   1/*
   2 * zfcp device driver
   3 *
   4 * Tracking of manually configured LUNs and helper functions to
   5 * register the LUNs with the SCSI midlayer.
   6 *
   7 * Copyright IBM Corp. 2010
   8 */
   9
  10#include "zfcp_def.h"
  11#include "zfcp_ext.h"
  12
  13/**
  14 * zfcp_unit_scsi_scan - Register LUN with SCSI midlayer
  15 * @unit: The zfcp LUN/unit to register
  16 *
  17 * When the SCSI midlayer is not allowed to automatically scan and
  18 * attach SCSI devices, zfcp has to register the single devices with
  19 * the SCSI midlayer.
  20 */
  21void zfcp_unit_scsi_scan(struct zfcp_unit *unit)
  22{
  23        struct fc_rport *rport = unit->port->rport;
  24        u64 lun;
  25
  26        lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun);
  27
  28        if (rport && rport->port_state == FC_PORTSTATE_ONLINE)
  29                scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, lun, 1);
  30}
  31
  32static void zfcp_unit_scsi_scan_work(struct work_struct *work)
  33{
  34        struct zfcp_unit *unit = container_of(work, struct zfcp_unit,
  35                                              scsi_work);
  36
  37        zfcp_unit_scsi_scan(unit);
  38        put_device(&unit->dev);
  39}
  40
  41/**
  42 * zfcp_unit_queue_scsi_scan - Register configured units on port
  43 * @port: The zfcp_port where to register units
  44 *
  45 * After opening a port, all units configured on this port have to be
  46 * registered with the SCSI midlayer. This function should be called
  47 * after calling fc_remote_port_add, so that the fc_rport is already
  48 * ONLINE and the call to scsi_scan_target runs the same way as the
  49 * call in the FC transport class.
  50 */
  51void zfcp_unit_queue_scsi_scan(struct zfcp_port *port)
  52{
  53        struct zfcp_unit *unit;
  54
  55        read_lock_irq(&port->unit_list_lock);
  56        list_for_each_entry(unit, &port->unit_list, list) {
  57                get_device(&unit->dev);
  58                if (scsi_queue_work(port->adapter->scsi_host,
  59                                    &unit->scsi_work) <= 0)
  60                        put_device(&unit->dev);
  61        }
  62        read_unlock_irq(&port->unit_list_lock);
  63}
  64
  65static struct zfcp_unit *_zfcp_unit_find(struct zfcp_port *port, u64 fcp_lun)
  66{
  67        struct zfcp_unit *unit;
  68
  69        list_for_each_entry(unit, &port->unit_list, list)
  70                if (unit->fcp_lun == fcp_lun) {
  71                        get_device(&unit->dev);
  72                        return unit;
  73                }
  74
  75        return NULL;
  76}
  77
  78/**
  79 * zfcp_unit_find - Find and return zfcp_unit with specified FCP LUN
  80 * @port: zfcp_port where to look for the unit
  81 * @fcp_lun: 64 Bit FCP LUN used to identify the zfcp_unit
  82 *
  83 * If zfcp_unit is found, a reference is acquired that has to be
  84 * released later.
  85 *
  86 * Returns: Pointer to the zfcp_unit, or NULL if there is no zfcp_unit
  87 *          with the specified FCP LUN.
  88 */
  89struct zfcp_unit *zfcp_unit_find(struct zfcp_port *port, u64 fcp_lun)
  90{
  91        struct zfcp_unit *unit;
  92
  93        read_lock_irq(&port->unit_list_lock);
  94        unit = _zfcp_unit_find(port, fcp_lun);
  95        read_unlock_irq(&port->unit_list_lock);
  96        return unit;
  97}
  98
  99/**
 100 * zfcp_unit_release - Drop reference to zfcp_port and free memory of zfcp_unit.
 101 * @dev: pointer to device in zfcp_unit
 102 */
 103static void zfcp_unit_release(struct device *dev)
 104{
 105        struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
 106
 107        atomic_dec(&unit->port->units);
 108        kfree(unit);
 109}
 110
 111/**
 112 * zfcp_unit_enqueue - enqueue unit to unit list of a port.
 113 * @port: pointer to port where unit is added
 114 * @fcp_lun: FCP LUN of unit to be enqueued
 115 * Returns: 0 success
 116 *
 117 * Sets up some unit internal structures and creates sysfs entry.
 118 */
 119int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
 120{
 121        struct zfcp_unit *unit;
 122        int retval = 0;
 123
 124        mutex_lock(&zfcp_sysfs_port_units_mutex);
 125        if (atomic_read(&port->units) == -1) {
 126                /* port is already gone */
 127                retval = -ENODEV;
 128                goto out;
 129        }
 130
 131        unit = zfcp_unit_find(port, fcp_lun);
 132        if (unit) {
 133                put_device(&unit->dev);
 134                retval = -EEXIST;
 135                goto out;
 136        }
 137
 138        unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
 139        if (!unit) {
 140                retval = -ENOMEM;
 141                goto out;
 142        }
 143
 144        unit->port = port;
 145        unit->fcp_lun = fcp_lun;
 146        unit->dev.parent = &port->dev;
 147        unit->dev.release = zfcp_unit_release;
 148        unit->dev.groups = zfcp_unit_attr_groups;
 149        INIT_WORK(&unit->scsi_work, zfcp_unit_scsi_scan_work);
 150
 151        if (dev_set_name(&unit->dev, "0x%016llx",
 152                         (unsigned long long) fcp_lun)) {
 153                kfree(unit);
 154                retval = -ENOMEM;
 155                goto out;
 156        }
 157
 158        if (device_register(&unit->dev)) {
 159                put_device(&unit->dev);
 160                retval = -ENOMEM;
 161                goto out;
 162        }
 163
 164        atomic_inc(&port->units); /* under zfcp_sysfs_port_units_mutex ! */
 165
 166        write_lock_irq(&port->unit_list_lock);
 167        list_add_tail(&unit->list, &port->unit_list);
 168        write_unlock_irq(&port->unit_list_lock);
 169
 170        zfcp_unit_scsi_scan(unit);
 171
 172out:
 173        mutex_unlock(&zfcp_sysfs_port_units_mutex);
 174        return retval;
 175}
 176
 177/**
 178 * zfcp_unit_sdev - Return SCSI device for zfcp_unit
 179 * @unit: The zfcp_unit where to get the SCSI device for
 180 *
 181 * Returns: scsi_device pointer on success, NULL if there is no SCSI
 182 *          device for this zfcp_unit
 183 *
 184 * On success, the caller also holds a reference to the SCSI device
 185 * that must be released with scsi_device_put.
 186 */
 187struct scsi_device *zfcp_unit_sdev(struct zfcp_unit *unit)
 188{
 189        struct Scsi_Host *shost;
 190        struct zfcp_port *port;
 191        u64 lun;
 192
 193        lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun);
 194        port = unit->port;
 195        shost = port->adapter->scsi_host;
 196        return scsi_device_lookup(shost, 0, port->starget_id, lun);
 197}
 198
 199/**
 200 * zfcp_unit_sdev_status - Return zfcp LUN status for SCSI device
 201 * @unit: The unit to lookup the SCSI device for
 202 *
 203 * Returns the zfcp LUN status field of the SCSI device if the SCSI device
 204 * for the zfcp_unit exists, 0 otherwise.
 205 */
 206unsigned int zfcp_unit_sdev_status(struct zfcp_unit *unit)
 207{
 208        unsigned int status = 0;
 209        struct scsi_device *sdev;
 210        struct zfcp_scsi_dev *zfcp_sdev;
 211
 212        sdev = zfcp_unit_sdev(unit);
 213        if (sdev) {
 214                zfcp_sdev = sdev_to_zfcp(sdev);
 215                status = atomic_read(&zfcp_sdev->status);
 216                scsi_device_put(sdev);
 217        }
 218
 219        return status;
 220}
 221
 222/**
 223 * zfcp_unit_remove - Remove entry from list of configured units
 224 * @port: The port where to remove the unit from the configuration
 225 * @fcp_lun: The 64 bit LUN of the unit to remove
 226 *
 227 * Returns: -EINVAL if a unit with the specified LUN does not exist,
 228 *          0 on success.
 229 */
 230int zfcp_unit_remove(struct zfcp_port *port, u64 fcp_lun)
 231{
 232        struct zfcp_unit *unit;
 233        struct scsi_device *sdev;
 234
 235        write_lock_irq(&port->unit_list_lock);
 236        unit = _zfcp_unit_find(port, fcp_lun);
 237        if (unit)
 238                list_del(&unit->list);
 239        write_unlock_irq(&port->unit_list_lock);
 240
 241        if (!unit)
 242                return -EINVAL;
 243
 244        sdev = zfcp_unit_sdev(unit);
 245        if (sdev) {
 246                scsi_remove_device(sdev);
 247                scsi_device_put(sdev);
 248        }
 249
 250        put_device(&unit->dev);
 251
 252        device_unregister(&unit->dev);
 253
 254        return 0;
 255}
 256