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