diff options
author | Chris Leech <christopher.leech@intel.com> | 2009-08-25 17:00:07 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-09-10 13:07:35 -0400 |
commit | 54b649f88eb17a29687bece4b8ad7d72d99e2d95 (patch) | |
tree | 0057186411aae1ad53268c127effa65f16f454e0 | |
parent | 030f4e001f13e0ee80bac1e756013341b1674d10 (diff) |
[SCSI] fcoe: split out per interface setup
fcoe_netdev_config() is called during initialization of a libfc instance.
Much of what was there only needs to be done once for each net_device.
The same goes for the corresponding cleanup.
The FIP controller initialization is moved to interface creation time.
Otherwise it will keep getting re-initialized for every VN_Port once NPIV is
enabled.
fcoe_if_destroy() has some reordering to deal with the changes. Receives are
not stopped until after fcoe_interface_put() is called, but transmits must be
stopped before. So there is some care to stop libfc transmits and the
transmit backlog timer, then call fcoe_interface_put which will stop receives
and cleanup the FIP controller, then the receive queues can be cleaned and the
port freed.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 267 |
1 files changed, 148 insertions, 119 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index d1d6b3b8bf5b..63aeeca384ba 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c | |||
@@ -138,6 +138,82 @@ static struct scsi_host_template fcoe_shost_template = { | |||
138 | .max_sectors = 0xffff, | 138 | .max_sectors = 0xffff, |
139 | }; | 139 | }; |
140 | 140 | ||
141 | static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, | ||
142 | struct packet_type *ptype, | ||
143 | struct net_device *orig_dev); | ||
144 | /** | ||
145 | * fcoe_interface_setup() | ||
146 | * @fcoe: new fcoe_interface | ||
147 | * @netdev : ptr to the associated netdevice struct | ||
148 | * | ||
149 | * Returns : 0 for success | ||
150 | */ | ||
151 | static int fcoe_interface_setup(struct fcoe_interface *fcoe, | ||
152 | struct net_device *netdev) | ||
153 | { | ||
154 | struct fcoe_ctlr *fip = &fcoe->ctlr; | ||
155 | struct netdev_hw_addr *ha; | ||
156 | u8 flogi_maddr[ETH_ALEN]; | ||
157 | |||
158 | fcoe->netdev = netdev; | ||
159 | |||
160 | /* Do not support for bonding device */ | ||
161 | if ((netdev->priv_flags & IFF_MASTER_ALB) || | ||
162 | (netdev->priv_flags & IFF_SLAVE_INACTIVE) || | ||
163 | (netdev->priv_flags & IFF_MASTER_8023AD)) { | ||
164 | return -EOPNOTSUPP; | ||
165 | } | ||
166 | |||
167 | /* look for SAN MAC address, if multiple SAN MACs exist, only | ||
168 | * use the first one for SPMA */ | ||
169 | rcu_read_lock(); | ||
170 | for_each_dev_addr(netdev, ha) { | ||
171 | if ((ha->type == NETDEV_HW_ADDR_T_SAN) && | ||
172 | (is_valid_ether_addr(fip->ctl_src_addr))) { | ||
173 | memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN); | ||
174 | fip->spma = 1; | ||
175 | break; | ||
176 | } | ||
177 | } | ||
178 | rcu_read_unlock(); | ||
179 | |||
180 | /* setup Source Mac Address */ | ||
181 | if (!fip->spma) | ||
182 | memcpy(fip->ctl_src_addr, netdev->dev_addr, netdev->addr_len); | ||
183 | |||
184 | /* | ||
185 | * Add FCoE MAC address as second unicast MAC address | ||
186 | * or enter promiscuous mode if not capable of listening | ||
187 | * for multiple unicast MACs. | ||
188 | */ | ||
189 | rtnl_lock(); | ||
190 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | ||
191 | dev_unicast_add(netdev, flogi_maddr); | ||
192 | if (fip->spma) | ||
193 | dev_unicast_add(netdev, fip->ctl_src_addr); | ||
194 | dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); | ||
195 | rtnl_unlock(); | ||
196 | |||
197 | /* | ||
198 | * setup the receive function from ethernet driver | ||
199 | * on the ethertype for the given device | ||
200 | */ | ||
201 | fcoe->fcoe_packet_type.func = fcoe_rcv; | ||
202 | fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); | ||
203 | fcoe->fcoe_packet_type.dev = netdev; | ||
204 | dev_add_pack(&fcoe->fcoe_packet_type); | ||
205 | |||
206 | fcoe->fip_packet_type.func = fcoe_fip_recv; | ||
207 | fcoe->fip_packet_type.type = htons(ETH_P_FIP); | ||
208 | fcoe->fip_packet_type.dev = netdev; | ||
209 | dev_add_pack(&fcoe->fip_packet_type); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb); | ||
215 | static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new); | ||
216 | |||
141 | /** | 217 | /** |
142 | * fcoe_interface_create() | 218 | * fcoe_interface_create() |
143 | * @netdev: network interface | 219 | * @netdev: network interface |
@@ -155,12 +231,55 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) | |||
155 | } | 231 | } |
156 | 232 | ||
157 | kref_init(&fcoe->kref); | 233 | kref_init(&fcoe->kref); |
158 | fcoe->netdev = netdev; | 234 | |
235 | /* | ||
236 | * Initialize FIP. | ||
237 | */ | ||
238 | fcoe_ctlr_init(&fcoe->ctlr); | ||
239 | fcoe->ctlr.send = fcoe_fip_send; | ||
240 | fcoe->ctlr.update_mac = fcoe_update_src_mac; | ||
241 | |||
242 | fcoe_interface_setup(fcoe, netdev); | ||
159 | 243 | ||
160 | return fcoe; | 244 | return fcoe; |
161 | } | 245 | } |
162 | 246 | ||
163 | /** | 247 | /** |
248 | * fcoe_interface_cleanup() - clean up netdev configurations | ||
249 | * @fcoe: | ||
250 | */ | ||
251 | void fcoe_interface_cleanup(struct fcoe_interface *fcoe) | ||
252 | { | ||
253 | struct net_device *netdev = fcoe->netdev; | ||
254 | struct fcoe_ctlr *fip = &fcoe->ctlr; | ||
255 | u8 flogi_maddr[ETH_ALEN]; | ||
256 | |||
257 | /* | ||
258 | * Don't listen for Ethernet packets anymore. | ||
259 | * synchronize_net() ensures that the packet handlers are not running | ||
260 | * on another CPU. dev_remove_pack() would do that, this calls the | ||
261 | * unsyncronized version __dev_remove_pack() to avoid multiple delays. | ||
262 | */ | ||
263 | __dev_remove_pack(&fcoe->fcoe_packet_type); | ||
264 | __dev_remove_pack(&fcoe->fip_packet_type); | ||
265 | synchronize_net(); | ||
266 | |||
267 | /* tear-down the FCoE controller */ | ||
268 | fcoe_ctlr_destroy(&fcoe->ctlr); | ||
269 | |||
270 | /* Delete secondary MAC addresses */ | ||
271 | rtnl_lock(); | ||
272 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | ||
273 | dev_unicast_delete(netdev, flogi_maddr); | ||
274 | if (!is_zero_ether_addr(fip->data_src_addr)) | ||
275 | dev_unicast_delete(netdev, fip->data_src_addr); | ||
276 | if (fip->spma) | ||
277 | dev_unicast_delete(netdev, fip->ctl_src_addr); | ||
278 | dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); | ||
279 | rtnl_unlock(); | ||
280 | } | ||
281 | |||
282 | /** | ||
164 | * fcoe_interface_release() - fcoe_port kref release function | 283 | * fcoe_interface_release() - fcoe_port kref release function |
165 | * @kref: embedded reference count in an fcoe_interface struct | 284 | * @kref: embedded reference count in an fcoe_interface struct |
166 | */ | 285 | */ |
@@ -169,6 +288,7 @@ static void fcoe_interface_release(struct kref *kref) | |||
169 | struct fcoe_interface *fcoe; | 288 | struct fcoe_interface *fcoe; |
170 | 289 | ||
171 | fcoe = container_of(kref, struct fcoe_interface, kref); | 290 | fcoe = container_of(kref, struct fcoe_interface, kref); |
291 | fcoe_interface_cleanup(fcoe); | ||
172 | kfree(fcoe); | 292 | kfree(fcoe); |
173 | } | 293 | } |
174 | 294 | ||
@@ -275,31 +395,6 @@ static int fcoe_lport_config(struct fc_lport *lp) | |||
275 | } | 395 | } |
276 | 396 | ||
277 | /** | 397 | /** |
278 | * fcoe_netdev_cleanup() - clean up netdev configurations | ||
279 | * @port: ptr to the fcoe_port | ||
280 | */ | ||
281 | void fcoe_netdev_cleanup(struct fcoe_port *port) | ||
282 | { | ||
283 | u8 flogi_maddr[ETH_ALEN]; | ||
284 | struct fcoe_interface *fcoe = port->fcoe; | ||
285 | |||
286 | /* Don't listen for Ethernet packets anymore */ | ||
287 | dev_remove_pack(&fcoe->fcoe_packet_type); | ||
288 | dev_remove_pack(&fcoe->fip_packet_type); | ||
289 | |||
290 | /* Delete secondary MAC addresses */ | ||
291 | rtnl_lock(); | ||
292 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | ||
293 | dev_unicast_delete(fcoe->netdev, flogi_maddr); | ||
294 | if (!is_zero_ether_addr(fcoe->ctlr.data_src_addr)) | ||
295 | dev_unicast_delete(fcoe->netdev, fcoe->ctlr.data_src_addr); | ||
296 | if (fcoe->ctlr.spma) | ||
297 | dev_unicast_delete(fcoe->netdev, fcoe->ctlr.ctl_src_addr); | ||
298 | dev_mc_delete(fcoe->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); | ||
299 | rtnl_unlock(); | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * fcoe_queue_timer() - fcoe queue timer | 398 | * fcoe_queue_timer() - fcoe queue timer |
304 | * @lp: the fc_lport pointer | 399 | * @lp: the fc_lport pointer |
305 | * | 400 | * |
@@ -326,21 +421,10 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) | |||
326 | u64 wwnn, wwpn; | 421 | u64 wwnn, wwpn; |
327 | struct fcoe_interface *fcoe; | 422 | struct fcoe_interface *fcoe; |
328 | struct fcoe_port *port; | 423 | struct fcoe_port *port; |
329 | u8 flogi_maddr[ETH_ALEN]; | ||
330 | struct netdev_hw_addr *ha; | ||
331 | 424 | ||
332 | /* Setup lport private data to point to fcoe softc */ | 425 | /* Setup lport private data to point to fcoe softc */ |
333 | port = lport_priv(lp); | 426 | port = lport_priv(lp); |
334 | fcoe = port->fcoe; | 427 | fcoe = port->fcoe; |
335 | fcoe->ctlr.lp = lp; | ||
336 | fcoe->netdev = netdev; | ||
337 | |||
338 | /* Do not support for bonding device */ | ||
339 | if ((netdev->priv_flags & IFF_MASTER_ALB) || | ||
340 | (netdev->priv_flags & IFF_SLAVE_INACTIVE) || | ||
341 | (netdev->priv_flags & IFF_MASTER_8023AD)) { | ||
342 | return -EOPNOTSUPP; | ||
343 | } | ||
344 | 428 | ||
345 | /* | 429 | /* |
346 | * Determine max frame size based on underlying device and optional | 430 | * Determine max frame size based on underlying device and optional |
@@ -376,57 +460,12 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) | |||
376 | port->fcoe_pending_queue_active = 0; | 460 | port->fcoe_pending_queue_active = 0; |
377 | setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp); | 461 | setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp); |
378 | 462 | ||
379 | /* look for SAN MAC address, if multiple SAN MACs exist, only | ||
380 | * use the first one for SPMA */ | ||
381 | rcu_read_lock(); | ||
382 | for_each_dev_addr(netdev, ha) { | ||
383 | if ((ha->type == NETDEV_HW_ADDR_T_SAN) && | ||
384 | (is_valid_ether_addr(fcoe->ctlr.ctl_src_addr))) { | ||
385 | memcpy(fcoe->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); | ||
386 | fcoe->ctlr.spma = 1; | ||
387 | break; | ||
388 | } | ||
389 | } | ||
390 | rcu_read_unlock(); | ||
391 | |||
392 | /* setup Source Mac Address */ | ||
393 | if (!fcoe->ctlr.spma) | ||
394 | memcpy(fcoe->ctlr.ctl_src_addr, netdev->dev_addr, | ||
395 | netdev->addr_len); | ||
396 | |||
397 | wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0); | 463 | wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0); |
398 | fc_set_wwnn(lp, wwnn); | 464 | fc_set_wwnn(lp, wwnn); |
399 | /* XXX - 3rd arg needs to be vlan id */ | 465 | /* XXX - 3rd arg needs to be vlan id */ |
400 | wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0); | 466 | wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0); |
401 | fc_set_wwpn(lp, wwpn); | 467 | fc_set_wwpn(lp, wwpn); |
402 | 468 | ||
403 | /* | ||
404 | * Add FCoE MAC address as second unicast MAC address | ||
405 | * or enter promiscuous mode if not capable of listening | ||
406 | * for multiple unicast MACs. | ||
407 | */ | ||
408 | rtnl_lock(); | ||
409 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | ||
410 | dev_unicast_add(netdev, flogi_maddr); | ||
411 | if (fcoe->ctlr.spma) | ||
412 | dev_unicast_add(netdev, fcoe->ctlr.ctl_src_addr); | ||
413 | dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); | ||
414 | rtnl_unlock(); | ||
415 | |||
416 | /* | ||
417 | * setup the receive function from ethernet driver | ||
418 | * on the ethertype for the given device | ||
419 | */ | ||
420 | fcoe->fcoe_packet_type.func = fcoe_rcv; | ||
421 | fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); | ||
422 | fcoe->fcoe_packet_type.dev = netdev; | ||
423 | dev_add_pack(&fcoe->fcoe_packet_type); | ||
424 | |||
425 | fcoe->fip_packet_type.func = fcoe_fip_recv; | ||
426 | fcoe->fip_packet_type.type = htons(ETH_P_FIP); | ||
427 | fcoe->fip_packet_type.dev = netdev; | ||
428 | dev_add_pack(&fcoe->fip_packet_type); | ||
429 | |||
430 | return 0; | 469 | return 0; |
431 | } | 470 | } |
432 | 471 | ||
@@ -578,19 +617,22 @@ static void fcoe_if_destroy(struct fc_lport *lport) | |||
578 | /* Remove the instance from fcoe's list */ | 617 | /* Remove the instance from fcoe's list */ |
579 | fcoe_hostlist_remove(lport); | 618 | fcoe_hostlist_remove(lport); |
580 | 619 | ||
581 | /* clean up netdev configurations */ | 620 | /* Cleanup the fc_lport */ |
582 | fcoe_netdev_cleanup(port); | 621 | fc_lport_destroy(lport); |
622 | fc_fcp_destroy(lport); | ||
623 | |||
624 | /* Stop the transmit retry timer */ | ||
625 | del_timer_sync(&port->timer); | ||
583 | 626 | ||
584 | /* tear-down the FCoE controller */ | 627 | /* Free existing transmit skbs */ |
585 | fcoe_ctlr_destroy(&fcoe->ctlr); | 628 | fcoe_clean_pending_queue(lport); |
629 | |||
630 | /* receives may not be stopped until after this */ | ||
631 | fcoe_interface_put(fcoe); | ||
586 | 632 | ||
587 | /* Free queued packets for the per-CPU receive threads */ | 633 | /* Free queued packets for the per-CPU receive threads */ |
588 | fcoe_percpu_clean(lport); | 634 | fcoe_percpu_clean(lport); |
589 | 635 | ||
590 | /* Cleanup the fc_lport */ | ||
591 | fc_lport_destroy(lport); | ||
592 | fc_fcp_destroy(lport); | ||
593 | |||
594 | /* Detach from the scsi-ml */ | 636 | /* Detach from the scsi-ml */ |
595 | fc_remove_host(lport->host); | 637 | fc_remove_host(lport->host); |
596 | scsi_remove_host(lport->host); | 638 | scsi_remove_host(lport->host); |
@@ -598,19 +640,12 @@ static void fcoe_if_destroy(struct fc_lport *lport) | |||
598 | /* There are no more rports or I/O, free the EM */ | 640 | /* There are no more rports or I/O, free the EM */ |
599 | fc_exch_mgr_free(lport); | 641 | fc_exch_mgr_free(lport); |
600 | 642 | ||
601 | /* Free existing skbs */ | ||
602 | fcoe_clean_pending_queue(lport); | ||
603 | |||
604 | /* Stop the timer */ | ||
605 | del_timer_sync(&port->timer); | ||
606 | |||
607 | /* Free memory used by statistical counters */ | 643 | /* Free memory used by statistical counters */ |
608 | fc_lport_free_stats(lport); | 644 | fc_lport_free_stats(lport); |
609 | 645 | ||
610 | /* Release the net_device and Scsi_Host */ | 646 | /* Release the net_device and Scsi_Host */ |
611 | dev_put(netdev); | 647 | dev_put(netdev); |
612 | scsi_host_put(lport->host); | 648 | scsi_host_put(lport->host); |
613 | fcoe_interface_put(fcoe); | ||
614 | } | 649 | } |
615 | 650 | ||
616 | /* | 651 | /* |
@@ -660,8 +695,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { | |||
660 | * @fcoe: fcoe_interface structure to create an fc_lport instance on | 695 | * @fcoe: fcoe_interface structure to create an fc_lport instance on |
661 | * @parent: device pointer to be the parent in sysfs for the SCSI host | 696 | * @parent: device pointer to be the parent in sysfs for the SCSI host |
662 | * | 697 | * |
663 | * Creates fc_lport struct and scsi_host for lport, configures lport | 698 | * Creates fc_lport struct and scsi_host for lport, configures lport. |
664 | * and starts fabric login. | ||
665 | * | 699 | * |
666 | * Returns : The allocated fc_lport or an error pointer | 700 | * Returns : The allocated fc_lport or an error pointer |
667 | */ | 701 | */ |
@@ -695,19 +729,12 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, | |||
695 | goto out_host_put; | 729 | goto out_host_put; |
696 | } | 730 | } |
697 | 731 | ||
698 | /* | ||
699 | * Initialize FIP. | ||
700 | */ | ||
701 | fcoe_ctlr_init(&fcoe->ctlr); | ||
702 | fcoe->ctlr.send = fcoe_fip_send; | ||
703 | fcoe->ctlr.update_mac = fcoe_update_src_mac; | ||
704 | |||
705 | /* configure lport network properties */ | 732 | /* configure lport network properties */ |
706 | rc = fcoe_netdev_config(lport, netdev); | 733 | rc = fcoe_netdev_config(lport, netdev); |
707 | if (rc) { | 734 | if (rc) { |
708 | FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the " | 735 | FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the " |
709 | "interface\n"); | 736 | "interface\n"); |
710 | goto out_netdev_cleanup; | 737 | goto out_lp_destroy; |
711 | } | 738 | } |
712 | 739 | ||
713 | /* configure lport scsi host properties */ | 740 | /* configure lport scsi host properties */ |
@@ -715,7 +742,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, | |||
715 | if (rc) { | 742 | if (rc) { |
716 | FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " | 743 | FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " |
717 | "interface\n"); | 744 | "interface\n"); |
718 | goto out_netdev_cleanup; | 745 | goto out_lp_destroy; |
719 | } | 746 | } |
720 | 747 | ||
721 | /* Initialize the library */ | 748 | /* Initialize the library */ |
@@ -745,21 +772,12 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, | |||
745 | fcoe_hostlist_add(lport); | 772 | fcoe_hostlist_add(lport); |
746 | write_unlock(&fcoe_hostlist_lock); | 773 | write_unlock(&fcoe_hostlist_lock); |
747 | 774 | ||
748 | lport->boot_time = jiffies; | ||
749 | |||
750 | fc_fabric_login(lport); | ||
751 | |||
752 | if (!fcoe_link_ok(lport)) | ||
753 | fcoe_ctlr_link_up(&fcoe->ctlr); | ||
754 | |||
755 | dev_hold(netdev); | 775 | dev_hold(netdev); |
756 | fcoe_interface_get(fcoe); | 776 | fcoe_interface_get(fcoe); |
757 | return lport; | 777 | return lport; |
758 | 778 | ||
759 | out_lp_destroy: | 779 | out_lp_destroy: |
760 | fc_exch_mgr_free(lport); | 780 | fc_exch_mgr_free(lport); |
761 | out_netdev_cleanup: | ||
762 | fcoe_netdev_cleanup(port); | ||
763 | out_host_put: | 781 | out_host_put: |
764 | scsi_host_put(lport->host); | 782 | scsi_host_put(lport->host); |
765 | out: | 783 | out: |
@@ -1731,10 +1749,21 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) | |||
1731 | goto out_free; | 1749 | goto out_free; |
1732 | } | 1750 | } |
1733 | 1751 | ||
1734 | dev_put(netdev); | 1752 | /* Make this the "master" N_Port */ |
1735 | return 0; | 1753 | fcoe->ctlr.lp = lport; |
1754 | |||
1755 | /* start FIP Discovery and FLOGI */ | ||
1756 | lport->boot_time = jiffies; | ||
1757 | fc_fabric_login(lport); | ||
1758 | if (!fcoe_link_ok(lport)) | ||
1759 | fcoe_ctlr_link_up(&fcoe->ctlr); | ||
1736 | 1760 | ||
1761 | rc = 0; | ||
1737 | out_free: | 1762 | out_free: |
1763 | /* | ||
1764 | * Release from init in fcoe_interface_create(), on success lport | ||
1765 | * should be holding a reference taken in fcoe_if_create(). | ||
1766 | */ | ||
1738 | fcoe_interface_put(fcoe); | 1767 | fcoe_interface_put(fcoe); |
1739 | out_putdev: | 1768 | out_putdev: |
1740 | dev_put(netdev); | 1769 | dev_put(netdev); |