1043 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright(c) 2011 - 2012 Intel Corporation. All rights reserved.4 *5 * Maintained at www.Open-FCoE.org6 */7 8#include <linux/module.h>9#include <linux/types.h>10#include <linux/kernel.h>11#include <linux/etherdevice.h>12#include <linux/ctype.h>13#include <linux/string.h>14 15#include <scsi/fcoe_sysfs.h>16#include <scsi/libfcoe.h>17 18/*19 * OK to include local libfcoe.h for debug_logging, but cannot include20 * <scsi/libfcoe.h> otherwise non-netdev based fcoe solutions would have21 * have to include more than fcoe_sysfs.h.22 */23#include "libfcoe.h"24 25static atomic_t ctlr_num;26static atomic_t fcf_num;27 28/*29 * fcoe_fcf_dev_loss_tmo: the default number of seconds that fcoe sysfs30 * should insulate the loss of a fcf.31 */32static unsigned int fcoe_fcf_dev_loss_tmo = 1800; /* seconds */33 34module_param_named(fcf_dev_loss_tmo, fcoe_fcf_dev_loss_tmo,35 uint, S_IRUGO|S_IWUSR);36MODULE_PARM_DESC(fcf_dev_loss_tmo,37 "Maximum number of seconds that libfcoe should"38 " insulate the loss of a fcf. Once this value is"39 " exceeded, the fcf is removed.");40 41/*42 * These are used by the fcoe_*_show_function routines, they43 * are intentionally placed in the .c file as they're not intended44 * for use throughout the code.45 */46#define fcoe_ctlr_id(x) \47 ((x)->id)48#define fcoe_ctlr_work_q(x) \49 ((x)->work_q)50#define fcoe_ctlr_devloss_work_q(x) \51 ((x)->devloss_work_q)52#define fcoe_ctlr_mode(x) \53 ((x)->mode)54#define fcoe_ctlr_fcf_dev_loss_tmo(x) \55 ((x)->fcf_dev_loss_tmo)56#define fcoe_ctlr_link_fail(x) \57 ((x)->lesb.lesb_link_fail)58#define fcoe_ctlr_vlink_fail(x) \59 ((x)->lesb.lesb_vlink_fail)60#define fcoe_ctlr_miss_fka(x) \61 ((x)->lesb.lesb_miss_fka)62#define fcoe_ctlr_symb_err(x) \63 ((x)->lesb.lesb_symb_err)64#define fcoe_ctlr_err_block(x) \65 ((x)->lesb.lesb_err_block)66#define fcoe_ctlr_fcs_error(x) \67 ((x)->lesb.lesb_fcs_error)68#define fcoe_ctlr_enabled(x) \69 ((x)->enabled)70#define fcoe_fcf_state(x) \71 ((x)->state)72#define fcoe_fcf_fabric_name(x) \73 ((x)->fabric_name)74#define fcoe_fcf_switch_name(x) \75 ((x)->switch_name)76#define fcoe_fcf_fc_map(x) \77 ((x)->fc_map)78#define fcoe_fcf_vfid(x) \79 ((x)->vfid)80#define fcoe_fcf_mac(x) \81 ((x)->mac)82#define fcoe_fcf_priority(x) \83 ((x)->priority)84#define fcoe_fcf_fka_period(x) \85 ((x)->fka_period)86#define fcoe_fcf_dev_loss_tmo(x) \87 ((x)->dev_loss_tmo)88#define fcoe_fcf_selected(x) \89 ((x)->selected)90#define fcoe_fcf_vlan_id(x) \91 ((x)->vlan_id)92 93/*94 * dev_loss_tmo attribute95 */96static int fcoe_str_to_dev_loss(const char *buf, unsigned long *val)97{98 int ret;99 100 ret = kstrtoul(buf, 0, val);101 if (ret)102 return -EINVAL;103 /*104 * Check for overflow; dev_loss_tmo is u32105 */106 if (*val > UINT_MAX)107 return -EINVAL;108 109 return 0;110}111 112static int fcoe_fcf_set_dev_loss_tmo(struct fcoe_fcf_device *fcf,113 unsigned long val)114{115 if ((fcf->state == FCOE_FCF_STATE_UNKNOWN) ||116 (fcf->state == FCOE_FCF_STATE_DISCONNECTED) ||117 (fcf->state == FCOE_FCF_STATE_DELETED))118 return -EBUSY;119 /*120 * Check for overflow; dev_loss_tmo is u32121 */122 if (val > UINT_MAX)123 return -EINVAL;124 125 fcoe_fcf_dev_loss_tmo(fcf) = val;126 return 0;127}128 129#define FCOE_DEVICE_ATTR(_prefix, _name, _mode, _show, _store) \130struct device_attribute device_attr_fcoe_##_prefix##_##_name = \131 __ATTR(_name, _mode, _show, _store)132 133#define fcoe_ctlr_show_function(field, format_string, sz, cast) \134static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \135 struct device_attribute *attr, \136 char *buf) \137{ \138 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \139 if (ctlr->f->get_fcoe_ctlr_##field) \140 ctlr->f->get_fcoe_ctlr_##field(ctlr); \141 return snprintf(buf, sz, format_string, \142 cast fcoe_ctlr_##field(ctlr)); \143}144 145#define fcoe_fcf_show_function(field, format_string, sz, cast) \146static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \147 struct device_attribute *attr, \148 char *buf) \149{ \150 struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \151 struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); \152 if (ctlr->f->get_fcoe_fcf_##field) \153 ctlr->f->get_fcoe_fcf_##field(fcf); \154 return snprintf(buf, sz, format_string, \155 cast fcoe_fcf_##field(fcf)); \156}157 158#define fcoe_ctlr_private_show_function(field, format_string, sz, cast) \159static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \160 struct device_attribute *attr, \161 char *buf) \162{ \163 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \164 return snprintf(buf, sz, format_string, cast fcoe_ctlr_##field(ctlr)); \165}166 167#define fcoe_fcf_private_show_function(field, format_string, sz, cast) \168static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \169 struct device_attribute *attr, \170 char *buf) \171{ \172 struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \173 return snprintf(buf, sz, format_string, cast fcoe_fcf_##field(fcf)); \174}175 176#define fcoe_ctlr_private_rd_attr(field, format_string, sz) \177 fcoe_ctlr_private_show_function(field, format_string, sz, ) \178 static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \179 show_fcoe_ctlr_device_##field, NULL)180 181#define fcoe_ctlr_rd_attr(field, format_string, sz) \182 fcoe_ctlr_show_function(field, format_string, sz, ) \183 static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \184 show_fcoe_ctlr_device_##field, NULL)185 186#define fcoe_fcf_rd_attr(field, format_string, sz) \187 fcoe_fcf_show_function(field, format_string, sz, ) \188 static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \189 show_fcoe_fcf_device_##field, NULL)190 191#define fcoe_fcf_private_rd_attr(field, format_string, sz) \192 fcoe_fcf_private_show_function(field, format_string, sz, ) \193 static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \194 show_fcoe_fcf_device_##field, NULL)195 196#define fcoe_ctlr_private_rd_attr_cast(field, format_string, sz, cast) \197 fcoe_ctlr_private_show_function(field, format_string, sz, (cast)) \198 static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \199 show_fcoe_ctlr_device_##field, NULL)200 201#define fcoe_fcf_private_rd_attr_cast(field, format_string, sz, cast) \202 fcoe_fcf_private_show_function(field, format_string, sz, (cast)) \203 static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \204 show_fcoe_fcf_device_##field, NULL)205 206#define fcoe_enum_name_search(title, table_type, table) \207static const char *get_fcoe_##title##_name(enum table_type table_key) \208{ \209 if (table_key < 0 || table_key >= ARRAY_SIZE(table)) \210 return NULL; \211 return table[table_key]; \212}213 214static const char * const fip_conn_type_names[] = {215 [ FIP_CONN_TYPE_UNKNOWN ] = "Unknown",216 [ FIP_CONN_TYPE_FABRIC ] = "Fabric",217 [ FIP_CONN_TYPE_VN2VN ] = "VN2VN",218};219fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names)220 221static char *fcf_state_names[] = {222 [ FCOE_FCF_STATE_UNKNOWN ] = "Unknown",223 [ FCOE_FCF_STATE_DISCONNECTED ] = "Disconnected",224 [ FCOE_FCF_STATE_CONNECTED ] = "Connected",225};226fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names)227#define FCOE_FCF_STATE_MAX_NAMELEN 50228 229static ssize_t show_fcf_state(struct device *dev,230 struct device_attribute *attr,231 char *buf)232{233 struct fcoe_fcf_device *fcf = dev_to_fcf(dev);234 const char *name;235 name = get_fcoe_fcf_state_name(fcf->state);236 if (!name)237 return -EINVAL;238 return snprintf(buf, FCOE_FCF_STATE_MAX_NAMELEN, "%s\n", name);239}240static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL);241 242#define FCOE_MAX_MODENAME_LEN 20243static ssize_t show_ctlr_mode(struct device *dev,244 struct device_attribute *attr,245 char *buf)246{247 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);248 const char *name;249 250 name = get_fcoe_ctlr_mode_name(ctlr->mode);251 if (!name)252 return -EINVAL;253 return snprintf(buf, FCOE_MAX_MODENAME_LEN,254 "%s\n", name);255}256 257static ssize_t store_ctlr_mode(struct device *dev,258 struct device_attribute *attr,259 const char *buf, size_t count)260{261 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);262 int res;263 264 if (count > FCOE_MAX_MODENAME_LEN)265 return -EINVAL;266 267 268 switch (ctlr->enabled) {269 case FCOE_CTLR_ENABLED:270 LIBFCOE_SYSFS_DBG(ctlr, "Cannot change mode when enabled.\n");271 return -EBUSY;272 case FCOE_CTLR_DISABLED:273 if (!ctlr->f->set_fcoe_ctlr_mode) {274 LIBFCOE_SYSFS_DBG(ctlr,275 "Mode change not supported by LLD.\n");276 return -ENOTSUPP;277 }278 279 res = sysfs_match_string(fip_conn_type_names, buf);280 if (res < 0 || res == FIP_CONN_TYPE_UNKNOWN) {281 LIBFCOE_SYSFS_DBG(ctlr, "Unknown mode %s provided.\n",282 buf);283 return -EINVAL;284 }285 ctlr->mode = res;286 287 ctlr->f->set_fcoe_ctlr_mode(ctlr);288 LIBFCOE_SYSFS_DBG(ctlr, "Mode changed to %s.\n", buf);289 290 return count;291 case FCOE_CTLR_UNUSED:292 default:293 LIBFCOE_SYSFS_DBG(ctlr, "Mode change not supported.\n");294 return -ENOTSUPP;295 }296}297 298static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO | S_IWUSR,299 show_ctlr_mode, store_ctlr_mode);300 301static ssize_t store_ctlr_enabled(struct device *dev,302 struct device_attribute *attr,303 const char *buf, size_t count)304{305 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);306 bool enabled;307 int rc;308 309 if (*buf == '1')310 enabled = true;311 else if (*buf == '0')312 enabled = false;313 else314 return -EINVAL;315 316 switch (ctlr->enabled) {317 case FCOE_CTLR_ENABLED:318 if (enabled)319 return count;320 ctlr->enabled = FCOE_CTLR_DISABLED;321 break;322 case FCOE_CTLR_DISABLED:323 if (!enabled)324 return count;325 ctlr->enabled = FCOE_CTLR_ENABLED;326 break;327 case FCOE_CTLR_UNUSED:328 return -ENOTSUPP;329 }330 331 rc = ctlr->f->set_fcoe_ctlr_enabled(ctlr);332 if (rc)333 return rc;334 335 return count;336}337 338static char *ctlr_enabled_state_names[] = {339 [ FCOE_CTLR_ENABLED ] = "1",340 [ FCOE_CTLR_DISABLED ] = "0",341};342fcoe_enum_name_search(ctlr_enabled_state, ctlr_enabled_state,343 ctlr_enabled_state_names)344#define FCOE_CTLR_ENABLED_MAX_NAMELEN 50345 346static ssize_t show_ctlr_enabled_state(struct device *dev,347 struct device_attribute *attr,348 char *buf)349{350 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);351 const char *name;352 353 name = get_fcoe_ctlr_enabled_state_name(ctlr->enabled);354 if (!name)355 return -EINVAL;356 return snprintf(buf, FCOE_CTLR_ENABLED_MAX_NAMELEN,357 "%s\n", name);358}359 360static FCOE_DEVICE_ATTR(ctlr, enabled, S_IRUGO | S_IWUSR,361 show_ctlr_enabled_state,362 store_ctlr_enabled);363 364static ssize_t store_ctlr_fip_resp(struct device *dev,365 struct device_attribute *attr,366 const char *buf, size_t count)367{368 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);369 struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr);370 371 mutex_lock(&fip->ctlr_mutex);372 if ((buf[1] == '\0') || ((buf[1] == '\n') && (buf[2] == '\0'))) {373 if (buf[0] == '1') {374 fip->fip_resp = 1;375 mutex_unlock(&fip->ctlr_mutex);376 return count;377 }378 if (buf[0] == '0') {379 fip->fip_resp = 0;380 mutex_unlock(&fip->ctlr_mutex);381 return count;382 }383 }384 mutex_unlock(&fip->ctlr_mutex);385 return -EINVAL;386}387 388static ssize_t show_ctlr_fip_resp(struct device *dev,389 struct device_attribute *attr,390 char *buf)391{392 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);393 struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr);394 395 return sprintf(buf, "%d\n", fip->fip_resp ? 1 : 0);396}397 398static FCOE_DEVICE_ATTR(ctlr, fip_vlan_responder, S_IRUGO | S_IWUSR,399 show_ctlr_fip_resp,400 store_ctlr_fip_resp);401 402static ssize_t403fcoe_ctlr_var_store(u32 *var, const char *buf, size_t count)404{405 int err;406 unsigned long v;407 408 err = kstrtoul(buf, 10, &v);409 if (err || v > UINT_MAX)410 return -EINVAL;411 412 *var = v;413 414 return count;415}416 417static ssize_t store_ctlr_r_a_tov(struct device *dev,418 struct device_attribute *attr,419 const char *buf, size_t count)420{421 struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);422 struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);423 424 if (ctlr_dev->enabled == FCOE_CTLR_ENABLED)425 return -EBUSY;426 if (ctlr_dev->enabled == FCOE_CTLR_DISABLED)427 return fcoe_ctlr_var_store(&ctlr->lp->r_a_tov, buf, count);428 return -ENOTSUPP;429}430 431static ssize_t show_ctlr_r_a_tov(struct device *dev,432 struct device_attribute *attr,433 char *buf)434{435 struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);436 struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);437 438 return sprintf(buf, "%d\n", ctlr->lp->r_a_tov);439}440 441static FCOE_DEVICE_ATTR(ctlr, r_a_tov, S_IRUGO | S_IWUSR,442 show_ctlr_r_a_tov, store_ctlr_r_a_tov);443 444static ssize_t store_ctlr_e_d_tov(struct device *dev,445 struct device_attribute *attr,446 const char *buf, size_t count)447{448 struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);449 struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);450 451 if (ctlr_dev->enabled == FCOE_CTLR_ENABLED)452 return -EBUSY;453 if (ctlr_dev->enabled == FCOE_CTLR_DISABLED)454 return fcoe_ctlr_var_store(&ctlr->lp->e_d_tov, buf, count);455 return -ENOTSUPP;456}457 458static ssize_t show_ctlr_e_d_tov(struct device *dev,459 struct device_attribute *attr,460 char *buf)461{462 struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);463 struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);464 465 return sprintf(buf, "%d\n", ctlr->lp->e_d_tov);466}467 468static FCOE_DEVICE_ATTR(ctlr, e_d_tov, S_IRUGO | S_IWUSR,469 show_ctlr_e_d_tov, store_ctlr_e_d_tov);470 471static ssize_t472store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev,473 struct device_attribute *attr,474 const char *buf, size_t count)475{476 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);477 struct fcoe_fcf_device *fcf;478 unsigned long val;479 int rc;480 481 rc = fcoe_str_to_dev_loss(buf, &val);482 if (rc)483 return rc;484 485 fcoe_ctlr_fcf_dev_loss_tmo(ctlr) = val;486 mutex_lock(&ctlr->lock);487 list_for_each_entry(fcf, &ctlr->fcfs, peers)488 fcoe_fcf_set_dev_loss_tmo(fcf, val);489 mutex_unlock(&ctlr->lock);490 return count;491}492fcoe_ctlr_private_show_function(fcf_dev_loss_tmo, "%d\n", 20, );493static FCOE_DEVICE_ATTR(ctlr, fcf_dev_loss_tmo, S_IRUGO | S_IWUSR,494 show_fcoe_ctlr_device_fcf_dev_loss_tmo,495 store_private_fcoe_ctlr_fcf_dev_loss_tmo);496 497/* Link Error Status Block (LESB) */498fcoe_ctlr_rd_attr(link_fail, "%u\n", 20);499fcoe_ctlr_rd_attr(vlink_fail, "%u\n", 20);500fcoe_ctlr_rd_attr(miss_fka, "%u\n", 20);501fcoe_ctlr_rd_attr(symb_err, "%u\n", 20);502fcoe_ctlr_rd_attr(err_block, "%u\n", 20);503fcoe_ctlr_rd_attr(fcs_error, "%u\n", 20);504 505fcoe_fcf_private_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);506fcoe_fcf_private_rd_attr_cast(switch_name, "0x%llx\n", 20, unsigned long long);507fcoe_fcf_private_rd_attr(priority, "%u\n", 20);508fcoe_fcf_private_rd_attr(fc_map, "0x%x\n", 20);509fcoe_fcf_private_rd_attr(vfid, "%u\n", 20);510fcoe_fcf_private_rd_attr(mac, "%pM\n", 20);511fcoe_fcf_private_rd_attr(fka_period, "%u\n", 20);512fcoe_fcf_rd_attr(selected, "%u\n", 20);513fcoe_fcf_rd_attr(vlan_id, "%u\n", 20);514 515fcoe_fcf_private_show_function(dev_loss_tmo, "%d\n", 20, )516static ssize_t517store_fcoe_fcf_dev_loss_tmo(struct device *dev, struct device_attribute *attr,518 const char *buf, size_t count)519{520 struct fcoe_fcf_device *fcf = dev_to_fcf(dev);521 unsigned long val;522 int rc;523 524 rc = fcoe_str_to_dev_loss(buf, &val);525 if (rc)526 return rc;527 528 rc = fcoe_fcf_set_dev_loss_tmo(fcf, val);529 if (rc)530 return rc;531 return count;532}533static FCOE_DEVICE_ATTR(fcf, dev_loss_tmo, S_IRUGO | S_IWUSR,534 show_fcoe_fcf_device_dev_loss_tmo,535 store_fcoe_fcf_dev_loss_tmo);536 537static struct attribute *fcoe_ctlr_lesb_attrs[] = {538 &device_attr_fcoe_ctlr_link_fail.attr,539 &device_attr_fcoe_ctlr_vlink_fail.attr,540 &device_attr_fcoe_ctlr_miss_fka.attr,541 &device_attr_fcoe_ctlr_symb_err.attr,542 &device_attr_fcoe_ctlr_err_block.attr,543 &device_attr_fcoe_ctlr_fcs_error.attr,544 NULL,545};546 547static struct attribute_group fcoe_ctlr_lesb_attr_group = {548 .name = "lesb",549 .attrs = fcoe_ctlr_lesb_attrs,550};551 552static struct attribute *fcoe_ctlr_attrs[] = {553 &device_attr_fcoe_ctlr_fip_vlan_responder.attr,554 &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr,555 &device_attr_fcoe_ctlr_r_a_tov.attr,556 &device_attr_fcoe_ctlr_e_d_tov.attr,557 &device_attr_fcoe_ctlr_enabled.attr,558 &device_attr_fcoe_ctlr_mode.attr,559 NULL,560};561 562static struct attribute_group fcoe_ctlr_attr_group = {563 .attrs = fcoe_ctlr_attrs,564};565 566static const struct attribute_group *fcoe_ctlr_attr_groups[] = {567 &fcoe_ctlr_attr_group,568 &fcoe_ctlr_lesb_attr_group,569 NULL,570};571 572static struct attribute *fcoe_fcf_attrs[] = {573 &device_attr_fcoe_fcf_fabric_name.attr,574 &device_attr_fcoe_fcf_switch_name.attr,575 &device_attr_fcoe_fcf_dev_loss_tmo.attr,576 &device_attr_fcoe_fcf_fc_map.attr,577 &device_attr_fcoe_fcf_vfid.attr,578 &device_attr_fcoe_fcf_mac.attr,579 &device_attr_fcoe_fcf_priority.attr,580 &device_attr_fcoe_fcf_fka_period.attr,581 &device_attr_fcoe_fcf_state.attr,582 &device_attr_fcoe_fcf_selected.attr,583 &device_attr_fcoe_fcf_vlan_id.attr,584 NULL585};586 587static struct attribute_group fcoe_fcf_attr_group = {588 .attrs = fcoe_fcf_attrs,589};590 591static const struct attribute_group *fcoe_fcf_attr_groups[] = {592 &fcoe_fcf_attr_group,593 NULL,594};595 596static const struct bus_type fcoe_bus_type;597 598static int fcoe_bus_match(struct device *dev,599 const struct device_driver *drv)600{601 if (dev->bus == &fcoe_bus_type)602 return 1;603 return 0;604}605 606/**607 * fcoe_ctlr_device_release() - Release the FIP ctlr memory608 * @dev: Pointer to the FIP ctlr's embedded device609 *610 * Called when the last FIP ctlr reference is released.611 */612static void fcoe_ctlr_device_release(struct device *dev)613{614 struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);615 kfree(ctlr);616}617 618/**619 * fcoe_fcf_device_release() - Release the FIP fcf memory620 * @dev: Pointer to the fcf's embedded device621 *622 * Called when the last FIP fcf reference is released.623 */624static void fcoe_fcf_device_release(struct device *dev)625{626 struct fcoe_fcf_device *fcf = dev_to_fcf(dev);627 kfree(fcf);628}629 630static const struct device_type fcoe_ctlr_device_type = {631 .name = "fcoe_ctlr",632 .groups = fcoe_ctlr_attr_groups,633 .release = fcoe_ctlr_device_release,634};635 636static const struct device_type fcoe_fcf_device_type = {637 .name = "fcoe_fcf",638 .groups = fcoe_fcf_attr_groups,639 .release = fcoe_fcf_device_release,640};641 642static ssize_t ctlr_create_store(const struct bus_type *bus, const char *buf,643 size_t count)644{645 return fcoe_ctlr_create_store(buf, count);646}647static BUS_ATTR_WO(ctlr_create);648 649static ssize_t ctlr_destroy_store(const struct bus_type *bus, const char *buf,650 size_t count)651{652 return fcoe_ctlr_destroy_store(buf, count);653}654static BUS_ATTR_WO(ctlr_destroy);655 656static struct attribute *fcoe_bus_attrs[] = {657 &bus_attr_ctlr_create.attr,658 &bus_attr_ctlr_destroy.attr,659 NULL,660};661ATTRIBUTE_GROUPS(fcoe_bus);662 663static const struct bus_type fcoe_bus_type = {664 .name = "fcoe",665 .match = &fcoe_bus_match,666 .bus_groups = fcoe_bus_groups,667};668 669/**670 * fcoe_ctlr_device_flush_work() - Flush a FIP ctlr's workqueue671 * @ctlr: Pointer to the FIP ctlr whose workqueue is to be flushed672 */673static void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr)674{675 if (!fcoe_ctlr_work_q(ctlr)) {676 printk(KERN_ERR677 "ERROR: FIP Ctlr '%d' attempted to flush work, "678 "when no workqueue created.\n", ctlr->id);679 dump_stack();680 return;681 }682 683 flush_workqueue(fcoe_ctlr_work_q(ctlr));684}685 686/**687 * fcoe_ctlr_device_queue_work() - Schedule work for a FIP ctlr's workqueue688 * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue689 * @work: Work to queue for execution690 *691 * Return value:692 * 1 on success / 0 already queued / < 0 for error693 */694static int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr,695 struct work_struct *work)696{697 if (unlikely(!fcoe_ctlr_work_q(ctlr))) {698 printk(KERN_ERR699 "ERROR: FIP Ctlr '%d' attempted to queue work, "700 "when no workqueue created.\n", ctlr->id);701 dump_stack();702 703 return -EINVAL;704 }705 706 return queue_work(fcoe_ctlr_work_q(ctlr), work);707}708 709/**710 * fcoe_ctlr_device_flush_devloss() - Flush a FIP ctlr's devloss workqueue711 * @ctlr: Pointer to FIP ctlr whose workqueue is to be flushed712 */713static void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr)714{715 if (!fcoe_ctlr_devloss_work_q(ctlr)) {716 printk(KERN_ERR717 "ERROR: FIP Ctlr '%d' attempted to flush work, "718 "when no workqueue created.\n", ctlr->id);719 dump_stack();720 return;721 }722 723 flush_workqueue(fcoe_ctlr_devloss_work_q(ctlr));724}725 726/**727 * fcoe_ctlr_device_queue_devloss_work() - Schedule work for a FIP ctlr's devloss workqueue728 * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue729 * @work: Work to queue for execution730 * @delay: jiffies to delay the work queuing731 *732 * Return value:733 * 1 on success / 0 already queued / < 0 for error734 */735static int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr,736 struct delayed_work *work,737 unsigned long delay)738{739 if (unlikely(!fcoe_ctlr_devloss_work_q(ctlr))) {740 printk(KERN_ERR741 "ERROR: FIP Ctlr '%d' attempted to queue work, "742 "when no workqueue created.\n", ctlr->id);743 dump_stack();744 745 return -EINVAL;746 }747 748 return queue_delayed_work(fcoe_ctlr_devloss_work_q(ctlr), work, delay);749}750 751static int fcoe_fcf_device_match(struct fcoe_fcf_device *new,752 struct fcoe_fcf_device *old)753{754 if (new->switch_name == old->switch_name &&755 new->fabric_name == old->fabric_name &&756 new->fc_map == old->fc_map &&757 ether_addr_equal(new->mac, old->mac))758 return 1;759 return 0;760}761 762/**763 * fcoe_ctlr_device_add() - Add a FIP ctlr to sysfs764 * @parent: The parent device to which the fcoe_ctlr instance765 * should be attached766 * @f: The LLD's FCoE sysfs function template pointer767 * @priv_size: Size to be allocated with the fcoe_ctlr_device for the LLD768 *769 * This routine allocates a FIP ctlr object with some additional memory770 * for the LLD. The FIP ctlr is initialized, added to sysfs and then771 * attributes are added to it.772 */773struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent,774 struct fcoe_sysfs_function_template *f,775 int priv_size)776{777 struct fcoe_ctlr_device *ctlr;778 int error = 0;779 780 ctlr = kzalloc(sizeof(struct fcoe_ctlr_device) + priv_size,781 GFP_KERNEL);782 if (!ctlr)783 goto out;784 785 ctlr->id = atomic_inc_return(&ctlr_num) - 1;786 ctlr->f = f;787 ctlr->mode = FIP_CONN_TYPE_FABRIC;788 INIT_LIST_HEAD(&ctlr->fcfs);789 mutex_init(&ctlr->lock);790 ctlr->dev.parent = parent;791 ctlr->dev.bus = &fcoe_bus_type;792 ctlr->dev.type = &fcoe_ctlr_device_type;793 794 ctlr->fcf_dev_loss_tmo = fcoe_fcf_dev_loss_tmo;795 796 ctlr->work_q = alloc_ordered_workqueue("ctlr_wq_%d", WQ_MEM_RECLAIM,797 ctlr->id);798 if (!ctlr->work_q)799 goto out_del;800 801 ctlr->devloss_work_q = alloc_ordered_workqueue("ctlr_dl_wq_%d",802 WQ_MEM_RECLAIM,803 ctlr->id);804 if (!ctlr->devloss_work_q)805 goto out_del_q;806 807 dev_set_name(&ctlr->dev, "ctlr_%d", ctlr->id);808 error = device_register(&ctlr->dev);809 if (error) {810 destroy_workqueue(ctlr->devloss_work_q);811 destroy_workqueue(ctlr->work_q);812 put_device(&ctlr->dev);813 return NULL;814 }815 816 return ctlr;817 818out_del_q:819 destroy_workqueue(ctlr->work_q);820 ctlr->work_q = NULL;821out_del:822 kfree(ctlr);823out:824 return NULL;825}826EXPORT_SYMBOL_GPL(fcoe_ctlr_device_add);827 828/**829 * fcoe_ctlr_device_delete() - Delete a FIP ctlr and its subtree from sysfs830 * @ctlr: A pointer to the ctlr to be deleted831 *832 * Deletes a FIP ctlr and any fcfs attached833 * to it. Deleting fcfs will cause their childen834 * to be deleted as well.835 *836 * The ctlr is detached from sysfs and it's resources837 * are freed (work q), but the memory is not freed838 * until its last reference is released.839 *840 * This routine expects no locks to be held before841 * calling.842 *843 * TODO: Currently there are no callbacks to clean up LLD data844 * for a fcoe_fcf_device. LLDs must keep this in mind as they need845 * to clean up each of their LLD data for all fcoe_fcf_device before846 * calling fcoe_ctlr_device_delete.847 */848void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *ctlr)849{850 struct fcoe_fcf_device *fcf, *next;851 /* Remove any attached fcfs */852 mutex_lock(&ctlr->lock);853 list_for_each_entry_safe(fcf, next,854 &ctlr->fcfs, peers) {855 list_del(&fcf->peers);856 fcf->state = FCOE_FCF_STATE_DELETED;857 fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);858 }859 mutex_unlock(&ctlr->lock);860 861 fcoe_ctlr_device_flush_work(ctlr);862 863 destroy_workqueue(ctlr->devloss_work_q);864 ctlr->devloss_work_q = NULL;865 destroy_workqueue(ctlr->work_q);866 ctlr->work_q = NULL;867 868 device_unregister(&ctlr->dev);869}870EXPORT_SYMBOL_GPL(fcoe_ctlr_device_delete);871 872/**873 * fcoe_fcf_device_final_delete() - Final delete routine874 * @work: The FIP fcf's embedded work struct875 *876 * It is expected that the fcf has been removed from877 * the FIP ctlr's list before calling this routine.878 */879static void fcoe_fcf_device_final_delete(struct work_struct *work)880{881 struct fcoe_fcf_device *fcf =882 container_of(work, struct fcoe_fcf_device, delete_work);883 struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);884 885 /*886 * Cancel any outstanding timers. These should really exist887 * only when rmmod'ing the LLDD and we're asking for888 * immediate termination of the rports889 */890 if (!cancel_delayed_work(&fcf->dev_loss_work))891 fcoe_ctlr_device_flush_devloss(ctlr);892 893 device_unregister(&fcf->dev);894}895 896/**897 * fip_timeout_deleted_fcf() - Delete a fcf when the devloss timer fires898 * @work: The FIP fcf's embedded work struct899 *900 * Removes the fcf from the FIP ctlr's list of fcfs and901 * queues the final deletion.902 */903static void fip_timeout_deleted_fcf(struct work_struct *work)904{905 struct fcoe_fcf_device *fcf =906 container_of(work, struct fcoe_fcf_device, dev_loss_work.work);907 struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);908 909 mutex_lock(&ctlr->lock);910 911 /*912 * If the fcf is deleted or reconnected before the timer913 * fires the devloss queue will be flushed, but the state will914 * either be CONNECTED or DELETED. If that is the case we915 * cancel deleting the fcf.916 */917 if (fcf->state != FCOE_FCF_STATE_DISCONNECTED)918 goto out;919 920 dev_printk(KERN_ERR, &fcf->dev,921 "FIP fcf connection time out: removing fcf\n");922 923 list_del(&fcf->peers);924 fcf->state = FCOE_FCF_STATE_DELETED;925 fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);926 927out:928 mutex_unlock(&ctlr->lock);929}930 931/**932 * fcoe_fcf_device_delete() - Delete a FIP fcf933 * @fcf: Pointer to the fcf which is to be deleted934 *935 * Queues the FIP fcf on the devloss workqueue936 *937 * Expects the ctlr_attrs mutex to be held for fcf938 * state change.939 */940void fcoe_fcf_device_delete(struct fcoe_fcf_device *fcf)941{942 struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);943 int timeout = fcf->dev_loss_tmo;944 945 if (fcf->state != FCOE_FCF_STATE_CONNECTED)946 return;947 948 fcf->state = FCOE_FCF_STATE_DISCONNECTED;949 950 /*951 * FCF will only be re-connected by the LLD calling952 * fcoe_fcf_device_add, and it should be setting up953 * priv then.954 */955 fcf->priv = NULL;956 957 fcoe_ctlr_device_queue_devloss_work(ctlr, &fcf->dev_loss_work,958 timeout * HZ);959}960EXPORT_SYMBOL_GPL(fcoe_fcf_device_delete);961 962/**963 * fcoe_fcf_device_add() - Add a FCoE sysfs fcoe_fcf_device to the system964 * @ctlr: The fcoe_ctlr_device that will be the fcoe_fcf_device parent965 * @new_fcf: A temporary FCF used for lookups on the current list of fcfs966 *967 * Expects to be called with the ctlr->lock held968 */969struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *ctlr,970 struct fcoe_fcf_device *new_fcf)971{972 struct fcoe_fcf_device *fcf;973 int error = 0;974 975 list_for_each_entry(fcf, &ctlr->fcfs, peers) {976 if (fcoe_fcf_device_match(new_fcf, fcf)) {977 if (fcf->state == FCOE_FCF_STATE_CONNECTED)978 return fcf;979 980 fcf->state = FCOE_FCF_STATE_CONNECTED;981 982 if (!cancel_delayed_work(&fcf->dev_loss_work))983 fcoe_ctlr_device_flush_devloss(ctlr);984 985 return fcf;986 }987 }988 989 fcf = kzalloc(sizeof(struct fcoe_fcf_device), GFP_ATOMIC);990 if (unlikely(!fcf))991 goto out;992 993 INIT_WORK(&fcf->delete_work, fcoe_fcf_device_final_delete);994 INIT_DELAYED_WORK(&fcf->dev_loss_work, fip_timeout_deleted_fcf);995 996 fcf->dev.parent = &ctlr->dev;997 fcf->dev.bus = &fcoe_bus_type;998 fcf->dev.type = &fcoe_fcf_device_type;999 fcf->id = atomic_inc_return(&fcf_num) - 1;1000 fcf->state = FCOE_FCF_STATE_UNKNOWN;1001 1002 fcf->dev_loss_tmo = ctlr->fcf_dev_loss_tmo;1003 1004 dev_set_name(&fcf->dev, "fcf_%d", fcf->id);1005 1006 fcf->fabric_name = new_fcf->fabric_name;1007 fcf->switch_name = new_fcf->switch_name;1008 fcf->fc_map = new_fcf->fc_map;1009 fcf->vfid = new_fcf->vfid;1010 memcpy(fcf->mac, new_fcf->mac, ETH_ALEN);1011 fcf->priority = new_fcf->priority;1012 fcf->fka_period = new_fcf->fka_period;1013 fcf->selected = new_fcf->selected;1014 1015 error = device_register(&fcf->dev);1016 if (error) {1017 put_device(&fcf->dev);1018 goto out;1019 }1020 1021 fcf->state = FCOE_FCF_STATE_CONNECTED;1022 list_add_tail(&fcf->peers, &ctlr->fcfs);1023 1024 return fcf;1025 1026out:1027 return NULL;1028}1029EXPORT_SYMBOL_GPL(fcoe_fcf_device_add);1030 1031int __init fcoe_sysfs_setup(void)1032{1033 atomic_set(&ctlr_num, 0);1034 atomic_set(&fcf_num, 0);1035 1036 return bus_register(&fcoe_bus_type);1037}1038 1039void __exit fcoe_sysfs_teardown(void)1040{1041 bus_unregister(&fcoe_bus_type);1042}1043