diff options
Diffstat (limited to 'drivers/scsi/fcoe')
-rw-r--r-- | drivers/scsi/fcoe/Makefile | 2 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 200 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe.h | 8 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe_ctlr.c | 159 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe_sysfs.c | 832 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe_transport.c | 13 |
6 files changed, 1142 insertions, 72 deletions
diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile index f6d37d0271f7..aed0f5db3668 100644 --- a/drivers/scsi/fcoe/Makefile +++ b/drivers/scsi/fcoe/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_FCOE) += fcoe.o obj-$(CONFIG_LIBFCOE) += libfcoe.o -libfcoe-objs := fcoe_ctlr.o fcoe_transport.o +libfcoe-objs := fcoe_ctlr.o fcoe_transport.o fcoe_sysfs.o diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 76e3d0b5bfa6..fe30b1b65e1d 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -41,6 +41,7 @@ #include <scsi/fc/fc_encaps.h> #include <scsi/fc/fc_fip.h> +#include <scsi/fc/fc_fcoe.h> #include <scsi/libfc.h> #include <scsi/fc_frame.h> @@ -150,6 +151,21 @@ static int fcoe_vport_create(struct fc_vport *, bool disabled); static int fcoe_vport_disable(struct fc_vport *, bool disable); static void fcoe_set_vport_symbolic_name(struct fc_vport *); static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *); +static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *); +static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); + +static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { + .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode, + .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb, + + .get_fcoe_fcf_selected = fcoe_fcf_get_selected, + .get_fcoe_fcf_vlan_id = fcoe_fcf_get_vlan_id, +}; static struct libfc_function_template fcoe_libfc_fcn_templ = { .frame_send = fcoe_xmit, @@ -282,7 +298,7 @@ static struct scsi_host_template fcoe_shost_template = { static int fcoe_interface_setup(struct fcoe_interface *fcoe, struct net_device *netdev) { - struct fcoe_ctlr *fip = &fcoe->ctlr; + struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); struct netdev_hw_addr *ha; struct net_device *real_dev; u8 flogi_maddr[ETH_ALEN]; @@ -366,7 +382,10 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev, enum fip_state fip_mode) { + struct fcoe_ctlr_device *ctlr_dev; + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; + int size; int err; if (!try_module_get(THIS_MODULE)) { @@ -376,27 +395,32 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev, goto out; } - fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); - if (!fcoe) { - FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n"); + size = sizeof(struct fcoe_ctlr) + sizeof(struct fcoe_interface); + ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &fcoe_sysfs_templ, + size); + if (!ctlr_dev) { + FCOE_DBG("Failed to add fcoe_ctlr_device\n"); fcoe = ERR_PTR(-ENOMEM); goto out_putmod; } + ctlr = fcoe_ctlr_device_priv(ctlr_dev); + fcoe = fcoe_ctlr_priv(ctlr); + dev_hold(netdev); /* * Initialize FIP. */ - fcoe_ctlr_init(&fcoe->ctlr, fip_mode); - fcoe->ctlr.send = fcoe_fip_send; - fcoe->ctlr.update_mac = fcoe_update_src_mac; - fcoe->ctlr.get_src_addr = fcoe_get_src_mac; + fcoe_ctlr_init(ctlr, fip_mode); + ctlr->send = fcoe_fip_send; + ctlr->update_mac = fcoe_update_src_mac; + ctlr->get_src_addr = fcoe_get_src_mac; err = fcoe_interface_setup(fcoe, netdev); if (err) { - fcoe_ctlr_destroy(&fcoe->ctlr); - kfree(fcoe); + fcoe_ctlr_destroy(ctlr); + fcoe_ctlr_device_delete(ctlr_dev); dev_put(netdev); fcoe = ERR_PTR(err); goto out_putmod; @@ -419,7 +443,7 @@ out: static void fcoe_interface_remove(struct fcoe_interface *fcoe) { struct net_device *netdev = fcoe->netdev; - struct fcoe_ctlr *fip = &fcoe->ctlr; + struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); u8 flogi_maddr[ETH_ALEN]; const struct net_device_ops *ops; @@ -462,7 +486,8 @@ static void fcoe_interface_remove(struct fcoe_interface *fcoe) static void fcoe_interface_cleanup(struct fcoe_interface *fcoe) { struct net_device *netdev = fcoe->netdev; - struct fcoe_ctlr *fip = &fcoe->ctlr; + struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); + struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip); rtnl_lock(); if (!fcoe->removed) @@ -472,8 +497,8 @@ static void fcoe_interface_cleanup(struct fcoe_interface *fcoe) /* Release the self-reference taken during fcoe_interface_create() */ /* tear-down the FCoE controller */ fcoe_ctlr_destroy(fip); - scsi_host_put(fcoe->ctlr.lp->host); - kfree(fcoe); + scsi_host_put(fip->lp->host); + fcoe_ctlr_device_delete(ctlr_dev); dev_put(netdev); module_put(THIS_MODULE); } @@ -493,9 +518,11 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev, struct net_device *orig_dev) { struct fcoe_interface *fcoe; + struct fcoe_ctlr *ctlr; fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type); - fcoe_ctlr_recv(&fcoe->ctlr, skb); + ctlr = fcoe_to_ctlr(fcoe); + fcoe_ctlr_recv(ctlr, skb); return 0; } @@ -645,11 +672,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) u32 mfs; u64 wwnn, wwpn; struct fcoe_interface *fcoe; + struct fcoe_ctlr *ctlr; struct fcoe_port *port; /* Setup lport private data to point to fcoe softc */ port = lport_priv(lport); fcoe = port->priv; + ctlr = fcoe_to_ctlr(fcoe); /* * Determine max frame size based on underlying device and optional @@ -676,10 +705,10 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) if (!lport->vport) { if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) - wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0); + wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0); fc_set_wwnn(lport, wwnn); if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN)) - wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, + wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 2, 0); fc_set_wwpn(lport, wwpn); } @@ -1056,6 +1085,7 @@ static int fcoe_ddp_done(struct fc_lport *lport, u16 xid) static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, struct device *parent, int npiv) { + struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); struct net_device *netdev = fcoe->netdev; struct fc_lport *lport, *n_port; struct fcoe_port *port; @@ -1119,7 +1149,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, } /* Initialize the library */ - rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1); + rc = fcoe_libfc_config(lport, ctlr, &fcoe_libfc_fcn_templ, 1); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " "interface\n"); @@ -1386,6 +1416,7 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, { struct fc_lport *lport; struct fcoe_rcv_info *fr; + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct fc_frame_header *fh; struct fcoe_percpu_s *fps; @@ -1393,7 +1424,8 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, unsigned int cpu; fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); - lport = fcoe->ctlr.lp; + ctlr = fcoe_to_ctlr(fcoe); + lport = ctlr->lp; if (unlikely(!lport)) { FCOE_NETDEV_DBG(netdev, "Cannot find hba structure"); goto err2; @@ -1409,8 +1441,8 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, eh = eth_hdr(skb); - if (is_fip_mode(&fcoe->ctlr) && - compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) { + if (is_fip_mode(ctlr) && + compare_ether_addr(eh->h_source, ctlr->dest_addr)) { FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n", eh->h_source); goto err; @@ -1544,6 +1576,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) unsigned int elen; /* eth header, may include vlan */ struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->priv; + struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); u8 sof, eof; struct fcoe_hdr *hp; @@ -1559,7 +1592,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) } if (unlikely(fh->fh_type == FC_TYPE_ELS) && - fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb)) + fcoe_ctlr_els_send(ctlr, lport, skb)) return 0; sof = fr_sof(fp); @@ -1623,12 +1656,12 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) /* fill up mac and fcoe headers */ eh = eth_hdr(skb); eh->h_proto = htons(ETH_P_FCOE); - memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN); - if (fcoe->ctlr.map_dest) + memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN); + if (ctlr->map_dest) memcpy(eh->h_dest + 3, fh->fh_d_id, 3); - if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN)) - memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN); + if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN)) + memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN); else memcpy(eh->h_source, port->data_src_addr, ETH_ALEN); @@ -1677,6 +1710,7 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb) static inline int fcoe_filter_frames(struct fc_lport *lport, struct fc_frame *fp) { + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct fc_frame_header *fh; struct sk_buff *skb = (struct sk_buff *)fp; @@ -1698,7 +1732,8 @@ static inline int fcoe_filter_frames(struct fc_lport *lport, return 0; fcoe = ((struct fcoe_port *)lport_priv(lport))->priv; - if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO && + ctlr = fcoe_to_ctlr(fcoe); + if (is_fip_mode(ctlr) && fc_frame_payload_op(fp) == ELS_LOGO && ntoh24(fh->fh_s_id) == FC_FID_FLOGI) { FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n"); return -EINVAL; @@ -1877,6 +1912,7 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, ulong event, void *ptr) { struct dcb_app_type *entry = ptr; + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct net_device *netdev; struct fcoe_port *port; @@ -1894,6 +1930,8 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, if (!fcoe) return NOTIFY_OK; + ctlr = fcoe_to_ctlr(fcoe); + if (entry->dcbx & DCB_CAP_DCBX_VER_CEE) prio = ffs(entry->app.priority) - 1; else @@ -1904,10 +1942,10 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, if (entry->app.protocol == ETH_P_FIP || entry->app.protocol == ETH_P_FCOE) - fcoe->ctlr.priority = prio; + ctlr->priority = prio; if (entry->app.protocol == ETH_P_FCOE) { - port = lport_priv(fcoe->ctlr.lp); + port = lport_priv(ctlr->lp); port->priority = prio; } @@ -1929,6 +1967,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, { struct fc_lport *lport = NULL; struct net_device *netdev = ptr; + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct fcoe_port *port; struct fcoe_dev_stats *stats; @@ -1938,7 +1977,8 @@ static int fcoe_device_notification(struct notifier_block *notifier, list_for_each_entry(fcoe, &fcoe_hostlist, list) { if (fcoe->netdev == netdev) { - lport = fcoe->ctlr.lp; + ctlr = fcoe_to_ctlr(fcoe); + lport = ctlr->lp; break; } } @@ -1967,7 +2007,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, break; case NETDEV_UNREGISTER: list_del(&fcoe->list); - port = lport_priv(fcoe->ctlr.lp); + port = lport_priv(ctlr->lp); queue_work(fcoe_wq, &port->destroy_work); goto out; break; @@ -1982,8 +2022,8 @@ static int fcoe_device_notification(struct notifier_block *notifier, fcoe_link_speed_update(lport); if (link_possible && !fcoe_link_ok(lport)) - fcoe_ctlr_link_up(&fcoe->ctlr); - else if (fcoe_ctlr_link_down(&fcoe->ctlr)) { + fcoe_ctlr_link_up(ctlr); + else if (fcoe_ctlr_link_down(ctlr)) { stats = per_cpu_ptr(lport->dev_stats, get_cpu()); stats->LinkFailureCount++; put_cpu(); @@ -2003,6 +2043,7 @@ out: */ static int fcoe_disable(struct net_device *netdev) { + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; int rc = 0; @@ -2013,8 +2054,9 @@ static int fcoe_disable(struct net_device *netdev) rtnl_unlock(); if (fcoe) { - fcoe_ctlr_link_down(&fcoe->ctlr); - fcoe_clean_pending_queue(fcoe->ctlr.lp); + ctlr = fcoe_to_ctlr(fcoe); + fcoe_ctlr_link_down(ctlr); + fcoe_clean_pending_queue(ctlr->lp); } else rc = -ENODEV; @@ -2032,6 +2074,7 @@ static int fcoe_disable(struct net_device *netdev) */ static int fcoe_enable(struct net_device *netdev) { + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; int rc = 0; @@ -2040,11 +2083,17 @@ static int fcoe_enable(struct net_device *netdev) fcoe = fcoe_hostlist_lookup_port(netdev); rtnl_unlock(); - if (!fcoe) + if (!fcoe) { rc = -ENODEV; - else if (!fcoe_link_ok(fcoe->ctlr.lp)) - fcoe_ctlr_link_up(&fcoe->ctlr); + goto out; + } + + ctlr = fcoe_to_ctlr(fcoe); + + if (!fcoe_link_ok(ctlr->lp)) + fcoe_ctlr_link_up(ctlr); +out: mutex_unlock(&fcoe_config_mutex); return rc; } @@ -2059,6 +2108,7 @@ static int fcoe_enable(struct net_device *netdev) */ static int fcoe_destroy(struct net_device *netdev) { + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct fc_lport *lport; struct fcoe_port *port; @@ -2071,7 +2121,8 @@ static int fcoe_destroy(struct net_device *netdev) rc = -ENODEV; goto out_nodev; } - lport = fcoe->ctlr.lp; + ctlr = fcoe_to_ctlr(fcoe); + lport = ctlr->lp; port = lport_priv(lport); list_del(&fcoe->list); queue_work(fcoe_wq, &port->destroy_work); @@ -2126,7 +2177,8 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) int dcbx; u8 fup, up; struct net_device *netdev = fcoe->realdev; - struct fcoe_port *port = lport_priv(fcoe->ctlr.lp); + struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); + struct fcoe_port *port = lport_priv(ctlr->lp); struct dcb_app app = { .priority = 0, .protocol = ETH_P_FCOE @@ -2149,7 +2201,7 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) } port->priority = ffs(up) ? ffs(up) - 1 : 0; - fcoe->ctlr.priority = ffs(fup) ? ffs(fup) - 1 : port->priority; + ctlr->priority = ffs(fup) ? ffs(fup) - 1 : port->priority; } #endif } @@ -2166,6 +2218,8 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) { int rc = 0; + struct fcoe_ctlr_device *ctlr_dev; + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct fc_lport *lport; @@ -2184,7 +2238,9 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) goto out_nodev; } - lport = fcoe_if_create(fcoe, &netdev->dev, 0); + ctlr = fcoe_to_ctlr(fcoe); + ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr); + lport = fcoe_if_create(fcoe, &ctlr_dev->dev, 0); if (IS_ERR(lport)) { printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); @@ -2195,7 +2251,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) } /* Make this the "master" N_Port */ - fcoe->ctlr.lp = lport; + ctlr->lp = lport; /* setup DCB priority attributes. */ fcoe_dcb_create(fcoe); @@ -2208,7 +2264,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) fc_fabric_login(lport); if (!fcoe_link_ok(lport)) { rtnl_unlock(); - fcoe_ctlr_link_up(&fcoe->ctlr); + fcoe_ctlr_link_up(ctlr); mutex_unlock(&fcoe_config_mutex); return rc; } @@ -2320,11 +2376,12 @@ static int fcoe_reset(struct Scsi_Host *shost) struct fc_lport *lport = shost_priv(shost); struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->priv; + struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); - fcoe_ctlr_link_down(&fcoe->ctlr); - fcoe_clean_pending_queue(fcoe->ctlr.lp); - if (!fcoe_link_ok(fcoe->ctlr.lp)) - fcoe_ctlr_link_up(&fcoe->ctlr); + fcoe_ctlr_link_down(ctlr); + fcoe_clean_pending_queue(ctlr->lp); + if (!fcoe_link_ok(ctlr->lp)) + fcoe_ctlr_link_up(ctlr); return 0; } @@ -2359,10 +2416,12 @@ fcoe_hostlist_lookup_port(const struct net_device *netdev) */ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { + struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; fcoe = fcoe_hostlist_lookup_port(netdev); - return (fcoe) ? fcoe->ctlr.lp : NULL; + ctlr = fcoe_to_ctlr(fcoe); + return (fcoe) ? ctlr->lp : NULL; } /** @@ -2466,6 +2525,7 @@ module_init(fcoe_init); static void __exit fcoe_exit(void) { struct fcoe_interface *fcoe, *tmp; + struct fcoe_ctlr *ctlr; struct fcoe_port *port; unsigned int cpu; @@ -2477,7 +2537,8 @@ static void __exit fcoe_exit(void) rtnl_lock(); list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) { list_del(&fcoe->list); - port = lport_priv(fcoe->ctlr.lp); + ctlr = fcoe_to_ctlr(fcoe); + port = lport_priv(ctlr->lp); queue_work(fcoe_wq, &port->destroy_work); } rtnl_unlock(); @@ -2573,7 +2634,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did, { struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->priv; - struct fcoe_ctlr *fip = &fcoe->ctlr; + struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); struct fc_frame_header *fh = fc_frame_header_get(fp); switch (op) { @@ -2730,6 +2791,40 @@ static void fcoe_get_lesb(struct fc_lport *lport, __fcoe_get_lesb(lport, fc_lesb, netdev); } +static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) +{ + struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); + struct net_device *netdev = fcoe_netdev(fip->lp); + struct fcoe_fc_els_lesb *fcoe_lesb; + struct fc_els_lesb fc_lesb; + + __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); + fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); + + ctlr_dev->lesb.lesb_link_fail = + ntohl(fcoe_lesb->lesb_link_fail); + ctlr_dev->lesb.lesb_vlink_fail = + ntohl(fcoe_lesb->lesb_vlink_fail); + ctlr_dev->lesb.lesb_miss_fka = + ntohl(fcoe_lesb->lesb_miss_fka); + ctlr_dev->lesb.lesb_symb_err = + ntohl(fcoe_lesb->lesb_symb_err); + ctlr_dev->lesb.lesb_err_block = + ntohl(fcoe_lesb->lesb_err_block); + ctlr_dev->lesb.lesb_fcs_error = + ntohl(fcoe_lesb->lesb_fcs_error); +} + +static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) +{ + struct fcoe_ctlr_device *ctlr_dev = + fcoe_fcf_dev_to_ctlr_dev(fcf_dev); + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); + struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr); + + fcf_dev->vlan_id = vlan_dev_vlan_id(fcoe->netdev); +} + /** * fcoe_set_port_id() - Callback from libfc when Port_ID is set. * @lport: the local port @@ -2747,7 +2842,8 @@ static void fcoe_set_port_id(struct fc_lport *lport, { struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->priv; + struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); if (fp && fc_frame_payload_op(fp) == ELS_FLOGI) - fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp); + fcoe_ctlr_recv_flogi(ctlr, lport, fp); } diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 96ac938d39cc..a624add4f8ec 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -68,7 +68,6 @@ do { \ * @netdev: The associated net device * @fcoe_packet_type: FCoE packet type * @fip_packet_type: FIP packet type - * @ctlr: The FCoE controller (for FIP) * @oem: The offload exchange manager for all local port * instances associated with this port * @removed: Indicates fcoe interface removed from net device @@ -80,12 +79,15 @@ struct fcoe_interface { struct net_device *realdev; struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; - struct fcoe_ctlr ctlr; struct fc_exch_mgr *oem; u8 removed; }; -#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) +#define fcoe_to_ctlr(x) \ + (struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1) + +#define fcoe_from_ctlr(x) \ + ((struct fcoe_interface *)((x) + 1)) /** * fcoe_netdev() - Return the net device associated with a local port diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 5a4c7250aa77..d68d57241ee6 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -160,6 +160,76 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode) } EXPORT_SYMBOL(fcoe_ctlr_init); +static int fcoe_sysfs_fcf_add(struct fcoe_fcf *new) +{ + struct fcoe_ctlr *fip = new->fip; + struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip); + struct fcoe_fcf_device temp, *fcf_dev; + int rc = 0; + + LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n", + new->fabric_name, new->fcf_mac); + + mutex_lock(&ctlr_dev->lock); + + temp.fabric_name = new->fabric_name; + temp.switch_name = new->switch_name; + temp.fc_map = new->fc_map; + temp.vfid = new->vfid; + memcpy(temp.mac, new->fcf_mac, ETH_ALEN); + temp.priority = new->pri; + temp.fka_period = new->fka_period; + temp.selected = 0; /* default to unselected */ + + fcf_dev = fcoe_fcf_device_add(ctlr_dev, &temp); + if (unlikely(!fcf_dev)) { + rc = -ENOMEM; + goto out; + } + + /* + * The fcoe_sysfs layer can return a CONNECTED fcf that + * has a priv (fcf was never deleted) or a CONNECTED fcf + * that doesn't have a priv (fcf was deleted). However, + * libfcoe will always delete FCFs before trying to add + * them. This is ensured because both recv_adv and + * age_fcfs are protected by the the fcoe_ctlr's mutex. + * This means that we should never get a FCF with a + * non-NULL priv pointer. + */ + BUG_ON(fcf_dev->priv); + + fcf_dev->priv = new; + new->fcf_dev = fcf_dev; + + list_add(&new->list, &fip->fcfs); + fip->fcf_count++; + +out: + mutex_unlock(&ctlr_dev->lock); + return rc; +} + +static void fcoe_sysfs_fcf_del(struct fcoe_fcf *new) +{ + struct fcoe_ctlr *fip = new->fip; + struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip); + struct fcoe_fcf_device *fcf_dev; + + list_del(&new->list); + fip->fcf_count--; + + mutex_lock(&ctlr_dev->lock); + + fcf_dev = fcoe_fcf_to_fcf_dev(new); + WARN_ON(!fcf_dev); + new->fcf_dev = NULL; + fcoe_fcf_device_delete(fcf_dev); + kfree(new); + + mutex_unlock(&ctlr_dev->lock); +} + /** * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller * @fip: The FCoE controller whose FCFs are to be reset @@ -173,10 +243,10 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip) fip->sel_fcf = NULL; list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { - list_del(&fcf->list); - kfree(fcf); + fcoe_sysfs_fcf_del(fcf); } - fip->fcf_count = 0; + WARN_ON(fip->fcf_count); + fip->sel_time = 0; } @@ -717,8 +787,11 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); unsigned long deadline; unsigned long sel_time = 0; + struct list_head del_list; struct fcoe_dev_stats *stats; + INIT_LIST_HEAD(&del_list); + stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu()); list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { @@ -739,10 +812,13 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) if (time_after_eq(jiffies, deadline)) { if (fip->sel_fcf == fcf) fip->sel_fcf = NULL; + /* + * Move to delete list so we can call + * fcoe_sysfs_fcf_del (which can sleep) + * after the put_cpu(). + */ list_del(&fcf->list); - WARN_ON(!fip->fcf_count); - fip->fcf_count--; - kfree(fcf); + list_add(&fcf->list, &del_list); stats->VLinkFailureCount++; } else { if (time_after(next_timer, deadline)) @@ -753,6 +829,12 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) } } put_cpu(); + + list_for_each_entry_safe(fcf, next, &del_list, list) { + /* Removes fcf from current list */ + fcoe_sysfs_fcf_del(fcf); + } + if (sel_time && !fip->sel_fcf && !fip->sel_time) { sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY); fip->sel_time = sel_time; @@ -903,23 +985,23 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) { struct fcoe_fcf *fcf; struct fcoe_fcf new; - struct fcoe_fcf *found; unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV); int first = 0; int mtu_valid; + int found = 0; + int rc = 0; if (fcoe_ctlr_parse_adv(fip, skb, &new)) return; mutex_lock(&fip->ctlr_mutex); first = list_empty(&fip->fcfs); - found = NULL; list_for_each_entry(fcf, &fip->fcfs, list) { if (fcf->switch_name == new.switch_name && fcf->fabric_name == new.fabric_name && fcf->fc_map == new.fc_map && compare_ether_addr(fcf->fcf_mac, new.fcf_mac) == 0) { - found = fcf; + found = 1; break; } } @@ -931,9 +1013,16 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) if (!fcf) goto out; - fip->fcf_count++; memcpy(fcf, &new, sizeof(new)); - list_add(&fcf->list, &fip->fcfs); + fcf->fip = fip; + rc = fcoe_sysfs_fcf_add(fcf); + if (rc) { + printk(KERN_ERR "Failed to allocate sysfs instance " + "for FCF, fab %16.16llx mac %pM\n", + new.fabric_name, new.fcf_mac); + kfree(fcf); + goto out; + } } else { /* * Update the FCF's keep-alive descriptor flags. @@ -954,6 +1043,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) fcf->fka_period = new.fka_period; memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN); } + mtu_valid = fcoe_ctlr_mtu_valid(fcf); fcf->time = jiffies; if (!found) @@ -996,6 +1086,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) time_before(fip->sel_time, fip->timer.expires)) mod_timer(&fip->timer, fip->sel_time); } + out: mutex_unlock(&fip->ctlr_mutex); } @@ -2718,9 +2809,9 @@ unlock: /** * fcoe_libfc_config() - Sets up libfc related properties for local port - * @lp: The local port to configure libfc for - * @fip: The FCoE controller in use by the local port - * @tt: The libfc function template + * @lport: The local port to configure libfc for + * @fip: The FCoE controller in use by the local port + * @tt: The libfc function template * @init_fcp: If non-zero, the FCP portion of libfc should be initialized * * Returns : 0 for success @@ -2753,3 +2844,43 @@ int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip, return 0; } EXPORT_SYMBOL_GPL(fcoe_libfc_config); + +void fcoe_fcf_get_selected(struct fcoe_fcf_device *fcf_dev) +{ + struct fcoe_ctlr_device *ctlr_dev = fcoe_fcf_dev_to_ctlr_dev(fcf_dev); + struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); + struct fcoe_fcf *fcf; + + mutex_lock(&fip->ctlr_mutex); + mutex_lock(&ctlr_dev->lock); + + fcf = fcoe_fcf_device_priv(fcf_dev); + if (fcf) + fcf_dev->selected = (fcf == fip->sel_fcf) ? 1 : 0; + else + fcf_dev->selected = 0; + + mutex_unlock(&ctlr_dev->lock); + mutex_unlock(&fip->ctlr_mutex); +} +EXPORT_SYMBOL(fcoe_fcf_get_selected); + +void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *ctlr_dev) +{ + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); + + mutex_lock(&ctlr->ctlr_mutex); + switch (ctlr->mode) { + case FIP_MODE_FABRIC: + ctlr_dev->mode = FIP_CONN_TYPE_FABRIC; + break; + case FIP_MODE_VN2VN: + ctlr_dev->mode = FIP_CONN_TYPE_VN2VN; + break; + default: + ctlr_dev->mode = FIP_CONN_TYPE_UNKNOWN; + break; + } + mutex_unlock(&ctlr->ctlr_mutex); +} +EXPORT_SYMBOL(fcoe_ctlr_get_fip_mode); diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c new file mode 100644 index 000000000000..2bc163198d33 --- /dev/null +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -0,0 +1,832 @@ +/* + * Copyright(c) 2011 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * Maintained at www.Open-FCoE.org + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/etherdevice.h> + +#include <scsi/fcoe_sysfs.h> + +static atomic_t ctlr_num; +static atomic_t fcf_num; + +/* + * fcoe_fcf_dev_loss_tmo: the default number of seconds that fcoe sysfs + * should insulate the loss of a fcf. + */ +static unsigned int fcoe_fcf_dev_loss_tmo = 1800; /* seconds */ + +module_param_named(fcf_dev_loss_tmo, fcoe_fcf_dev_loss_tmo, + uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(fcf_dev_loss_tmo, + "Maximum number of seconds that libfcoe should" + " insulate the loss of a fcf. Once this value is" + " exceeded, the fcf is removed."); + +/* + * These are used by the fcoe_*_show_function routines, they + * are intentionally placed in the .c file as they're not intended + * for use throughout the code. + */ +#define fcoe_ctlr_id(x) \ + ((x)->id) +#define fcoe_ctlr_work_q_name(x) \ + ((x)->work_q_name) +#define fcoe_ctlr_work_q(x) \ + ((x)->work_q) +#define fcoe_ctlr_devloss_work_q_name(x) \ + ((x)->devloss_work_q_name) +#define fcoe_ctlr_devloss_work_q(x) \ + ((x)->devloss_work_q) +#define fcoe_ctlr_mode(x) \ + ((x)->mode) +#define fcoe_ctlr_fcf_dev_loss_tmo(x) \ + ((x)->fcf_dev_loss_tmo) +#define fcoe_ctlr_link_fail(x) \ + ((x)->lesb.lesb_link_fail) +#define fcoe_ctlr_vlink_fail(x) \ + ((x)->lesb.lesb_vlink_fail) +#define fcoe_ctlr_miss_fka(x) \ + ((x)->lesb.lesb_miss_fka) +#define fcoe_ctlr_symb_err(x) \ + ((x)->lesb.lesb_symb_err) +#define fcoe_ctlr_err_block(x) \ + ((x)->lesb.lesb_err_block) +#define fcoe_ctlr_fcs_error(x) \ + ((x)->lesb.lesb_fcs_error) +#define fcoe_fcf_state(x) \ + ((x)->state) +#define fcoe_fcf_fabric_name(x) \ + ((x)->fabric_name) +#define fcoe_fcf_switch_name(x) \ + ((x)->switch_name) +#define fcoe_fcf_fc_map(x) \ + ((x)->fc_map) +#define fcoe_fcf_vfid(x) \ + ((x)->vfid) +#define fcoe_fcf_mac(x) \ + ((x)->mac) +#define fcoe_fcf_priority(x) \ + ((x)->priority) +#define fcoe_fcf_fka_period(x) \ + ((x)->fka_period) +#define fcoe_fcf_dev_loss_tmo(x) \ + ((x)->dev_loss_tmo) +#define fcoe_fcf_selected(x) \ + ((x)->selected) +#define fcoe_fcf_vlan_id(x) \ + ((x)->vlan_id) + +/* + * dev_loss_tmo attribute + */ +static int fcoe_str_to_dev_loss(const char *buf, unsigned long *val) +{ + int ret; + + ret = kstrtoul(buf, 0, val); + if (ret || *val < 0) + return -EINVAL; + /* + * Check for overflow; dev_loss_tmo is u32 + */ + if (*val > UINT_MAX) + return -EINVAL; + + return 0; +} + +static int fcoe_fcf_set_dev_loss_tmo(struct fcoe_fcf_device *fcf, + unsigned long val) +{ + if ((fcf->state == FCOE_FCF_STATE_UNKNOWN) || + (fcf->state == FCOE_FCF_STATE_DISCONNECTED) || + (fcf->state == FCOE_FCF_STATE_DELETED)) + return -EBUSY; + /* + * Check for overflow; dev_loss_tmo is u32 + */ + if (val > UINT_MAX) + return -EINVAL; + + fcoe_fcf_dev_loss_tmo(fcf) = val; + return 0; +} + +#define FCOE_DEVICE_ATTR(_prefix, _name, _mode, _show, _store) \ +struct device_attribute device_attr_fcoe_##_prefix##_##_name = \ + __ATTR(_name, _mode, _show, _store) + +#define fcoe_ctlr_show_function(field, format_string, sz, cast) \ +static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \ + if (ctlr->f->get_fcoe_ctlr_##field) \ + ctlr->f->get_fcoe_ctlr_##field(ctlr); \ + return snprintf(buf, sz, format_string, \ + cast fcoe_ctlr_##field(ctlr)); \ +} + +#define fcoe_fcf_show_function(field, format_string, sz, cast) \ +static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \ + struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); \ + if (ctlr->f->get_fcoe_fcf_##field) \ + ctlr->f->get_fcoe_fcf_##field(fcf); \ + return snprintf(buf, sz, format_string, \ + cast fcoe_fcf_##field(fcf)); \ +} + +#define fcoe_ctlr_private_show_function(field, format_string, sz, cast) \ +static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \ + return snprintf(buf, sz, format_string, cast fcoe_ctlr_##field(ctlr)); \ +} + +#define fcoe_fcf_private_show_function(field, format_string, sz, cast) \ +static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \ + return snprintf(buf, sz, format_string, cast fcoe_fcf_##field(fcf)); \ +} + +#define fcoe_ctlr_private_rd_attr(field, format_string, sz) \ + fcoe_ctlr_private_show_function(field, format_string, sz, ) \ + static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \ + show_fcoe_ctlr_device_##field, NULL) + +#define fcoe_ctlr_rd_attr(field, format_string, sz) \ + fcoe_ctlr_show_function(field, format_string, sz, ) \ + static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \ + show_fcoe_ctlr_device_##field, NULL) + +#define fcoe_fcf_rd_attr(field, format_string, sz) \ + fcoe_fcf_show_function(field, format_string, sz, ) \ + static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \ + show_fcoe_fcf_device_##field, NULL) + +#define fcoe_fcf_private_rd_attr(field, format_string, sz) \ + fcoe_fcf_private_show_function(field, format_string, sz, ) \ + static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \ + show_fcoe_fcf_device_##field, NULL) + +#define fcoe_ctlr_private_rd_attr_cast(field, format_string, sz, cast) \ + fcoe_ctlr_private_show_function(field, format_string, sz, (cast)) \ + static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \ + show_fcoe_ctlr_device_##field, NULL) + +#define fcoe_fcf_private_rd_attr_cast(field, format_string, sz, cast) \ + fcoe_fcf_private_show_function(field, format_string, sz, (cast)) \ + static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \ + show_fcoe_fcf_device_##field, NULL) + +#define fcoe_enum_name_search(title, table_type, table) \ +static const char *get_fcoe_##title##_name(enum table_type table_key) \ +{ \ + int i; \ + char *name = NULL; \ + \ + for (i = 0; i < ARRAY_SIZE(table); i++) { \ + if (table[i].value == table_key) { \ + name = table[i].name; \ + break; \ + } \ + } \ + return name; \ +} + +static struct { + enum fcf_state value; + char *name; +} fcf_state_names[] = { + { FCOE_FCF_STATE_UNKNOWN, "Unknown" }, + { FCOE_FCF_STATE_DISCONNECTED, "Disconnected" }, + { FCOE_FCF_STATE_CONNECTED, "Connected" }, +}; +fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names) +#define FCOE_FCF_STATE_MAX_NAMELEN 50 + +static ssize_t show_fcf_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fcoe_fcf_device *fcf = dev_to_fcf(dev); + const char *name; + name = get_fcoe_fcf_state_name(fcf->state); + if (!name) + return -EINVAL; + return snprintf(buf, FCOE_FCF_STATE_MAX_NAMELEN, "%s\n", name); +} +static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL); + +static struct { + enum fip_conn_type value; + char *name; +} fip_conn_type_names[] = { + { FIP_CONN_TYPE_UNKNOWN, "Unknown" }, + { FIP_CONN_TYPE_FABRIC, "Fabric" }, + { FIP_CONN_TYPE_VN2VN, "VN2VN" }, +}; +fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) +#define FCOE_CTLR_MODE_MAX_NAMELEN 50 + +static ssize_t show_ctlr_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + const char *name; + + if (ctlr->f->get_fcoe_ctlr_mode) + ctlr->f->get_fcoe_ctlr_mode(ctlr); + + name = get_fcoe_ctlr_mode_name(ctlr->mode); + if (!name) + return -EINVAL; + return snprintf(buf, FCOE_CTLR_MODE_MAX_NAMELEN, + "%s\n", name); +} +static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO, + show_ctlr_mode, NULL); + +static ssize_t +store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + struct fcoe_fcf_device *fcf; + unsigned long val; + int rc; + + rc = fcoe_str_to_dev_loss(buf, &val); + if (rc) + return rc; + + fcoe_ctlr_fcf_dev_loss_tmo(ctlr) = val; + mutex_lock(&ctlr->lock); + list_for_each_entry(fcf, &ctlr->fcfs, peers) + fcoe_fcf_set_dev_loss_tmo(fcf, val); + mutex_unlock(&ctlr->lock); + return count; +} +fcoe_ctlr_private_show_function(fcf_dev_loss_tmo, "%d\n", 20, ); +static FCOE_DEVICE_ATTR(ctlr, fcf_dev_loss_tmo, S_IRUGO | S_IWUSR, + show_fcoe_ctlr_device_fcf_dev_loss_tmo, + store_private_fcoe_ctlr_fcf_dev_loss_tmo); + +/* Link Error Status Block (LESB) */ +fcoe_ctlr_rd_attr(link_fail, "%u\n", 20); +fcoe_ctlr_rd_attr(vlink_fail, "%u\n", 20); +fcoe_ctlr_rd_attr(miss_fka, "%u\n", 20); +fcoe_ctlr_rd_attr(symb_err, "%u\n", 20); +fcoe_ctlr_rd_attr(err_block, "%u\n", 20); +fcoe_ctlr_rd_attr(fcs_error, "%u\n", 20); + +fcoe_fcf_private_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long); +fcoe_fcf_private_rd_attr_cast(switch_name, "0x%llx\n", 20, unsigned long long); +fcoe_fcf_private_rd_attr(priority, "%u\n", 20); +fcoe_fcf_private_rd_attr(fc_map, "0x%x\n", 20); +fcoe_fcf_private_rd_attr(vfid, "%u\n", 20); +fcoe_fcf_private_rd_attr(mac, "%pM\n", 20); +fcoe_fcf_private_rd_attr(fka_period, "%u\n", 20); +fcoe_fcf_rd_attr(selected, "%u\n", 20); +fcoe_fcf_rd_attr(vlan_id, "%u\n", 20); + +fcoe_fcf_private_show_function(dev_loss_tmo, "%d\n", 20, ) +static ssize_t +store_fcoe_fcf_dev_loss_tmo(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fcoe_fcf_device *fcf = dev_to_fcf(dev); + unsigned long val; + int rc; + + rc = fcoe_str_to_dev_loss(buf, &val); + if (rc) + return rc; + + rc = fcoe_fcf_set_dev_loss_tmo(fcf, val); + if (rc) + return rc; + return count; +} +static FCOE_DEVICE_ATTR(fcf, dev_loss_tmo, S_IRUGO | S_IWUSR, + show_fcoe_fcf_device_dev_loss_tmo, + store_fcoe_fcf_dev_loss_tmo); + +static struct attribute *fcoe_ctlr_lesb_attrs[] = { + &device_attr_fcoe_ctlr_link_fail.attr, + &device_attr_fcoe_ctlr_vlink_fail.attr, + &device_attr_fcoe_ctlr_miss_fka.attr, + &device_attr_fcoe_ctlr_symb_err.attr, + &device_attr_fcoe_ctlr_err_block.attr, + &device_attr_fcoe_ctlr_fcs_error.attr, + NULL, +}; + +static struct attribute_group fcoe_ctlr_lesb_attr_group = { + .name = "lesb", + .attrs = fcoe_ctlr_lesb_attrs, +}; + +static struct attribute *fcoe_ctlr_attrs[] = { + &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr, + &device_attr_fcoe_ctlr_mode.attr, + NULL, +}; + +static struct attribute_group fcoe_ctlr_attr_group = { + .attrs = fcoe_ctlr_attrs, +}; + +static const struct attribute_group *fcoe_ctlr_attr_groups[] = { + &fcoe_ctlr_attr_group, + &fcoe_ctlr_lesb_attr_group, + NULL, +}; + +static struct attribute *fcoe_fcf_attrs[] = { + &device_attr_fcoe_fcf_fabric_name.attr, + &device_attr_fcoe_fcf_switch_name.attr, + &device_attr_fcoe_fcf_dev_loss_tmo.attr, + &device_attr_fcoe_fcf_fc_map.attr, + &device_attr_fcoe_fcf_vfid.attr, + &device_attr_fcoe_fcf_mac.attr, + &device_attr_fcoe_fcf_priority.attr, + &device_attr_fcoe_fcf_fka_period.attr, + &device_attr_fcoe_fcf_state.attr, + &device_attr_fcoe_fcf_selected.attr, + &device_attr_fcoe_fcf_vlan_id.attr, + NULL +}; + +static struct attribute_group fcoe_fcf_attr_group = { + .attrs = fcoe_fcf_attrs, +}; + +static const struct attribute_group *fcoe_fcf_attr_groups[] = { + &fcoe_fcf_attr_group, + NULL, +}; + +struct bus_type fcoe_bus_type; + +static int fcoe_bus_match(struct device *dev, + struct device_driver *drv) +{ + if (dev->bus == &fcoe_bus_type) + return 1; + return 0; +} + +/** + * fcoe_ctlr_device_release() - Release the FIP ctlr memory + * @dev: Pointer to the FIP ctlr's embedded device + * + * Called when the last FIP ctlr reference is released. + */ +static void fcoe_ctlr_device_release(struct device *dev) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + kfree(ctlr); +} + +/** + * fcoe_fcf_device_release() - Release the FIP fcf memory + * @dev: Pointer to the fcf's embedded device + * + * Called when the last FIP fcf reference is released. + */ +static void fcoe_fcf_device_release(struct device *dev) +{ + struct fcoe_fcf_device *fcf = dev_to_fcf(dev); + kfree(fcf); +} + +struct device_type fcoe_ctlr_device_type = { + .name = "fcoe_ctlr", + .groups = fcoe_ctlr_attr_groups, + .release = fcoe_ctlr_device_release, +}; + +struct device_type fcoe_fcf_device_type = { + .name = "fcoe_fcf", + .groups = fcoe_fcf_attr_groups, + .release = fcoe_fcf_device_release, +}; + +struct bus_type fcoe_bus_type = { + .name = "fcoe", + .match = &fcoe_bus_match, +}; + +/** + * fcoe_ctlr_device_flush_work() - Flush a FIP ctlr's workqueue + * @ctlr: Pointer to the FIP ctlr whose workqueue is to be flushed + */ +void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr) +{ + if (!fcoe_ctlr_work_q(ctlr)) { + printk(KERN_ERR + "ERROR: FIP Ctlr '%d' attempted to flush work, " + "when no workqueue created.\n", ctlr->id); + dump_stack(); + return; + } + + flush_workqueue(fcoe_ctlr_work_q(ctlr)); +} + +/** + * fcoe_ctlr_device_queue_work() - Schedule work for a FIP ctlr's workqueue + * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue + * @work: Work to queue for execution + * + * Return value: + * 1 on success / 0 already queued / < 0 for error + */ +int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr, + struct work_struct *work) +{ + if (unlikely(!fcoe_ctlr_work_q(ctlr))) { + printk(KERN_ERR + "ERROR: FIP Ctlr '%d' attempted to queue work, " + "when no workqueue created.\n", ctlr->id); + dump_stack(); + + return -EINVAL; + } + + return queue_work(fcoe_ctlr_work_q(ctlr), work); +} + +/** + * fcoe_ctlr_device_flush_devloss() - Flush a FIP ctlr's devloss workqueue + * @ctlr: Pointer to FIP ctlr whose workqueue is to be flushed + */ +void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr) +{ + if (!fcoe_ctlr_devloss_work_q(ctlr)) { + printk(KERN_ERR + "ERROR: FIP Ctlr '%d' attempted to flush work, " + "when no workqueue created.\n", ctlr->id); + dump_stack(); + return; + } + + flush_workqueue(fcoe_ctlr_devloss_work_q(ctlr)); +} + +/** + * fcoe_ctlr_device_queue_devloss_work() - Schedule work for a FIP ctlr's devloss workqueue + * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue + * @work: Work to queue for execution + * @delay: jiffies to delay the work queuing + * + * Return value: + * 1 on success / 0 already queued / < 0 for error + */ +int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr, + struct delayed_work *work, + unsigned long delay) +{ + if (unlikely(!fcoe_ctlr_devloss_work_q(ctlr))) { + printk(KERN_ERR + "ERROR: FIP Ctlr '%d' attempted to queue work, " + "when no workqueue created.\n", ctlr->id); + dump_stack(); + + return -EINVAL; + } + + return queue_delayed_work(fcoe_ctlr_devloss_work_q(ctlr), work, delay); +} + +static int fcoe_fcf_device_match(struct fcoe_fcf_device *new, + struct fcoe_fcf_device *old) +{ + if (new->switch_name == old->switch_name && + new->fabric_name == old->fabric_name && + new->fc_map == old->fc_map && + compare_ether_addr(new->mac, old->mac) == 0) + return 1; + return 0; +} + +/** + * fcoe_ctlr_device_add() - Add a FIP ctlr to sysfs + * @parent: The parent device to which the fcoe_ctlr instance + * should be attached + * @f: The LLD's FCoE sysfs function template pointer + * @priv_size: Size to be allocated with the fcoe_ctlr_device for the LLD + * + * This routine allocates a FIP ctlr object with some additional memory + * for the LLD. The FIP ctlr is initialized, added to sysfs and then + * attributes are added to it. + */ +struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent, + struct fcoe_sysfs_function_template *f, + int priv_size) +{ + struct fcoe_ctlr_device *ctlr; + int error = 0; + + ctlr = kzalloc(sizeof(struct fcoe_ctlr_device) + priv_size, + GFP_KERNEL); + if (!ctlr) + goto out; + + ctlr->id = atomic_inc_return(&ctlr_num) - 1; + ctlr->f = f; + INIT_LIST_HEAD(&ctlr->fcfs); + mutex_init(&ctlr->lock); + ctlr->dev.parent = parent; + ctlr->dev.bus = &fcoe_bus_type; + ctlr->dev.type = &fcoe_ctlr_device_type; + + ctlr->fcf_dev_loss_tmo = fcoe_fcf_dev_loss_tmo; + + snprintf(ctlr->work_q_name, sizeof(ctlr->work_q_name), + "ctlr_wq_%d", ctlr->id); + ctlr->work_q = create_singlethread_workqueue( + ctlr->work_q_name); + if (!ctlr->work_q) + goto out_del; + + snprintf(ctlr->devloss_work_q_name, + sizeof(ctlr->devloss_work_q_name), + "ctlr_dl_wq_%d", ctlr->id); + ctlr->devloss_work_q = create_singlethread_workqueue( + ctlr->devloss_work_q_name); + if (!ctlr->devloss_work_q) + goto out_del_q; + + dev_set_name(&ctlr->dev, "ctlr_%d", ctlr->id); + error = device_register(&ctlr->dev); + if (error) + goto out_del_q2; + + return ctlr; + +out_del_q2: + destroy_workqueue(ctlr->devloss_work_q); + ctlr->devloss_work_q = NULL; +out_del_q: + destroy_workqueue(ctlr->work_q); + ctlr->work_q = NULL; +out_del: + kfree(ctlr); +out: + return NULL; +} +EXPORT_SYMBOL_GPL(fcoe_ctlr_device_add); + +/** + * fcoe_ctlr_device_delete() - Delete a FIP ctlr and its subtree from sysfs + * @ctlr: A pointer to the ctlr to be deleted + * + * Deletes a FIP ctlr and any fcfs attached + * to it. Deleting fcfs will cause their childen + * to be deleted as well. + * + * The ctlr is detached from sysfs and it's resources + * are freed (work q), but the memory is not freed + * until its last reference is released. + * + * This routine expects no locks to be held before + * calling. + * + * TODO: Currently there are no callbacks to clean up LLD data + * for a fcoe_fcf_device. LLDs must keep this in mind as they need + * to clean up each of their LLD data for all fcoe_fcf_device before + * calling fcoe_ctlr_device_delete. + */ +void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *ctlr) +{ + struct fcoe_fcf_device *fcf, *next; + /* Remove any attached fcfs */ + mutex_lock(&ctlr->lock); + list_for_each_entry_safe(fcf, next, + &ctlr->fcfs, peers) { + list_del(&fcf->peers); + fcf->state = FCOE_FCF_STATE_DELETED; + fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work); + } + mutex_unlock(&ctlr->lock); + + fcoe_ctlr_device_flush_work(ctlr); + + destroy_workqueue(ctlr->devloss_work_q); + ctlr->devloss_work_q = NULL; + destroy_workqueue(ctlr->work_q); + ctlr->work_q = NULL; + + device_unregister(&ctlr->dev); +} +EXPORT_SYMBOL_GPL(fcoe_ctlr_device_delete); + +/** + * fcoe_fcf_device_final_delete() - Final delete routine + * @work: The FIP fcf's embedded work struct + * + * It is expected that the fcf has been removed from + * the FIP ctlr's list before calling this routine. + */ +static void fcoe_fcf_device_final_delete(struct work_struct *work) +{ + struct fcoe_fcf_device *fcf = + container_of(work, struct fcoe_fcf_device, delete_work); + struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); + + /* + * Cancel any outstanding timers. These should really exist + * only when rmmod'ing the LLDD and we're asking for + * immediate termination of the rports + */ + if (!cancel_delayed_work(&fcf->dev_loss_work)) + fcoe_ctlr_device_flush_devloss(ctlr); + + device_unregister(&fcf->dev); +} + +/** + * fip_timeout_deleted_fcf() - Delete a fcf when the devloss timer fires + * @work: The FIP fcf's embedded work struct + * + * Removes the fcf from the FIP ctlr's list of fcfs and + * queues the final deletion. + */ +static void fip_timeout_deleted_fcf(struct work_struct *work) +{ + struct fcoe_fcf_device *fcf = + container_of(work, struct fcoe_fcf_device, dev_loss_work.work); + struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); + + mutex_lock(&ctlr->lock); + + /* + * If the fcf is deleted or reconnected before the timer + * fires the devloss queue will be flushed, but the state will + * either be CONNECTED or DELETED. If that is the case we + * cancel deleting the fcf. + */ + if (fcf->state != FCOE_FCF_STATE_DISCONNECTED) + goto out; + + dev_printk(KERN_ERR, &fcf->dev, + "FIP fcf connection time out: removing fcf\n"); + + list_del(&fcf->peers); + fcf->state = FCOE_FCF_STATE_DELETED; + fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work); + +out: + mutex_unlock(&ctlr->lock); +} + +/** + * fcoe_fcf_device_delete() - Delete a FIP fcf + * @fcf: Pointer to the fcf which is to be deleted + * + * Queues the FIP fcf on the devloss workqueue + * + * Expects the ctlr_attrs mutex to be held for fcf + * state change. + */ +void fcoe_fcf_device_delete(struct fcoe_fcf_device *fcf) +{ + struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); + int timeout = fcf->dev_loss_tmo; + + if (fcf->state != FCOE_FCF_STATE_CONNECTED) + return; + + fcf->state = FCOE_FCF_STATE_DISCONNECTED; + + /* + * FCF will only be re-connected by the LLD calling + * fcoe_fcf_device_add, and it should be setting up + * priv then. + */ + fcf->priv = NULL; + + fcoe_ctlr_device_queue_devloss_work(ctlr, &fcf->dev_loss_work, + timeout * HZ); +} +EXPORT_SYMBOL_GPL(fcoe_fcf_device_delete); + +/** + * fcoe_fcf_device_add() - Add a FCoE sysfs fcoe_fcf_device to the system + * @ctlr: The fcoe_ctlr_device that will be the fcoe_fcf_device parent + * @new_fcf: A temporary FCF used for lookups on the current list of fcfs + * + * Expects to be called with the ctlr->lock held + */ +struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *ctlr, + struct fcoe_fcf_device *new_fcf) +{ + struct fcoe_fcf_device *fcf; + int error = 0; + + list_for_each_entry(fcf, &ctlr->fcfs, peers) { + if (fcoe_fcf_device_match(new_fcf, fcf)) { + if (fcf->state == FCOE_FCF_STATE_CONNECTED) + return fcf; + + fcf->state = FCOE_FCF_STATE_CONNECTED; + + if (!cancel_delayed_work(&fcf->dev_loss_work)) + fcoe_ctlr_device_flush_devloss(ctlr); + + return fcf; + } + } + + fcf = kzalloc(sizeof(struct fcoe_fcf_device), GFP_ATOMIC); + if (unlikely(!fcf)) + goto out; + + INIT_WORK(&fcf->delete_work, fcoe_fcf_device_final_delete); + INIT_DELAYED_WORK(&fcf->dev_loss_work, fip_timeout_deleted_fcf); + + fcf->dev.parent = &ctlr->dev; + fcf->dev.bus = &fcoe_bus_type; + fcf->dev.type = &fcoe_fcf_device_type; + fcf->id = atomic_inc_return(&fcf_num) - 1; + fcf->state = FCOE_FCF_STATE_UNKNOWN; + + fcf->dev_loss_tmo = ctlr->fcf_dev_loss_tmo; + + dev_set_name(&fcf->dev, "fcf_%d", fcf->id); + + fcf->fabric_name = new_fcf->fabric_name; + fcf->switch_name = new_fcf->switch_name; + fcf->fc_map = new_fcf->fc_map; + fcf->vfid = new_fcf->vfid; + memcpy(fcf->mac, new_fcf->mac, ETH_ALEN); + fcf->priority = new_fcf->priority; + fcf->fka_period = new_fcf->fka_period; + fcf->selected = new_fcf->selected; + + error = device_register(&fcf->dev); + if (error) + goto out_del; + + fcf->state = FCOE_FCF_STATE_CONNECTED; + list_add_tail(&fcf->peers, &ctlr->fcfs); + + return fcf; + +out_del: + kfree(fcf); +out: + return NULL; +} +EXPORT_SYMBOL_GPL(fcoe_fcf_device_add); + +int __init fcoe_sysfs_setup(void) +{ + int error; + + atomic_set(&ctlr_num, 0); + atomic_set(&fcf_num, 0); + + error = bus_register(&fcoe_bus_type); + if (error) + return error; + + return 0; +} + +void __exit fcoe_sysfs_teardown(void) +{ + bus_unregister(&fcoe_bus_type); +} diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 710e149d41b6..b46f43dced78 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -815,9 +815,17 @@ out_nodev: */ static int __init libfcoe_init(void) { - fcoe_transport_init(); + int rc = 0; - return 0; + rc = fcoe_transport_init(); + if (rc) + return rc; + + rc = fcoe_sysfs_setup(); + if (rc) + fcoe_transport_exit(); + + return rc; } module_init(libfcoe_init); @@ -826,6 +834,7 @@ module_init(libfcoe_init); */ static void __exit libfcoe_exit(void) { + fcoe_sysfs_teardown(); fcoe_transport_exit(); } module_exit(libfcoe_exit); |