diff options
Diffstat (limited to 'drivers/scsi/fcoe/libfcoe.c')
| -rw-r--r-- | drivers/scsi/fcoe/libfcoe.c | 1510 |
1 files changed, 0 insertions, 1510 deletions
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c deleted file mode 100644 index e419f486cdb..00000000000 --- a/drivers/scsi/fcoe/libfcoe.c +++ /dev/null @@ -1,1510 +0,0 @@ -/* - * Copyright(c) 2007 - 2008 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/version.h> -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/ethtool.h> -#include <linux/if_ether.h> -#include <linux/if_vlan.h> -#include <linux/kthread.h> -#include <linux/crc32.h> -#include <linux/cpu.h> -#include <linux/fs.h> -#include <linux/sysfs.h> -#include <linux/ctype.h> -#include <scsi/scsi_tcq.h> -#include <scsi/scsicam.h> -#include <scsi/scsi_transport.h> -#include <scsi/scsi_transport_fc.h> -#include <net/rtnetlink.h> - -#include <scsi/fc/fc_encaps.h> - -#include <scsi/libfc.h> -#include <scsi/fc_frame.h> -#include <scsi/libfcoe.h> -#include <scsi/fc_transport_fcoe.h> - -static int debug_fcoe; - -#define FCOE_MAX_QUEUE_DEPTH 256 - -/* destination address mode */ -#define FCOE_GW_ADDR_MODE 0x00 -#define FCOE_FCOUI_ADDR_MODE 0x01 - -#define FCOE_WORD_TO_BYTE 4 - -MODULE_AUTHOR("Open-FCoE.org"); -MODULE_DESCRIPTION("FCoE"); -MODULE_LICENSE("GPL"); - -/* fcoe host list */ -LIST_HEAD(fcoe_hostlist); -DEFINE_RWLOCK(fcoe_hostlist_lock); -DEFINE_TIMER(fcoe_timer, NULL, 0, 0); -struct fcoe_percpu_s *fcoe_percpu[NR_CPUS]; - - -/* Function Prototyes */ -static int fcoe_check_wait_queue(struct fc_lport *); -static void fcoe_insert_wait_queue_head(struct fc_lport *, struct sk_buff *); -static void fcoe_insert_wait_queue(struct fc_lport *, struct sk_buff *); -static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *); -#ifdef CONFIG_HOTPLUG_CPU -static int fcoe_cpu_callback(struct notifier_block *, ulong, void *); -#endif /* CONFIG_HOTPLUG_CPU */ -static int fcoe_device_notification(struct notifier_block *, ulong, void *); -static void fcoe_dev_setup(void); -static void fcoe_dev_cleanup(void); - -/* notification function from net device */ -static struct notifier_block fcoe_notifier = { - .notifier_call = fcoe_device_notification, -}; - - -#ifdef CONFIG_HOTPLUG_CPU -static struct notifier_block fcoe_cpu_notifier = { - .notifier_call = fcoe_cpu_callback, -}; - -/** - * fcoe_create_percpu_data - creates the associated cpu data - * @cpu: index for the cpu where fcoe cpu data will be created - * - * create percpu stats block, from cpu add notifier - * - * Returns: none - **/ -static void fcoe_create_percpu_data(int cpu) -{ - struct fc_lport *lp; - struct fcoe_softc *fc; - - write_lock_bh(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - lp = fc->lp; - if (lp->dev_stats[cpu] == NULL) - lp->dev_stats[cpu] = - kzalloc(sizeof(struct fcoe_dev_stats), - GFP_KERNEL); - } - write_unlock_bh(&fcoe_hostlist_lock); -} - -/** - * fcoe_destroy_percpu_data - destroys the associated cpu data - * @cpu: index for the cpu where fcoe cpu data will destroyed - * - * destroy percpu stats block called by cpu add/remove notifier - * - * Retuns: none - **/ -static void fcoe_destroy_percpu_data(int cpu) -{ - struct fc_lport *lp; - struct fcoe_softc *fc; - - write_lock_bh(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - lp = fc->lp; - kfree(lp->dev_stats[cpu]); - lp->dev_stats[cpu] = NULL; - } - write_unlock_bh(&fcoe_hostlist_lock); -} - -/** - * fcoe_cpu_callback - fcoe cpu hotplug event callback - * @nfb: callback data block - * @action: event triggering the callback - * @hcpu: index for the cpu of this event - * - * this creates or destroys per cpu data for fcoe - * - * Returns NOTIFY_OK always. - **/ -static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action, - void *hcpu) -{ - unsigned int cpu = (unsigned long)hcpu; - - switch (action) { - case CPU_ONLINE: - fcoe_create_percpu_data(cpu); - break; - case CPU_DEAD: - fcoe_destroy_percpu_data(cpu); - break; - default: - break; - } - return NOTIFY_OK; -} -#endif /* CONFIG_HOTPLUG_CPU */ - -/** - * fcoe_rcv - this is the fcoe receive function called by NET_RX_SOFTIRQ - * @skb: the receive skb - * @dev: associated net device - * @ptype: context - * @odldev: last device - * - * this function will receive the packet and build fc frame and pass it up - * - * Returns: 0 for success - **/ -int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, struct net_device *olddev) -{ - struct fc_lport *lp; - struct fcoe_rcv_info *fr; - struct fcoe_softc *fc; - struct fcoe_dev_stats *stats; - struct fc_frame_header *fh; - unsigned short oxid; - int cpu_idx; - struct fcoe_percpu_s *fps; - - fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type); - lp = fc->lp; - if (unlikely(lp == NULL)) { - FC_DBG("cannot find hba structure"); - goto err2; - } - - if (unlikely(debug_fcoe)) { - FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p " - "end:%p sum:%d dev:%s", skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : "<NULL>"); - - } - - /* check for FCOE packet type */ - if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { - FC_DBG("wrong FC type frame"); - goto err; - } - - /* - * Check for minimum frame length, and make sure required FCoE - * and FC headers are pulled into the linear data area. - */ - if (unlikely((skb->len < FCOE_MIN_FRAME) || - !pskb_may_pull(skb, FCOE_HEADER_LEN))) - goto err; - - skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); - fh = (struct fc_frame_header *) skb_transport_header(skb); - - oxid = ntohs(fh->fh_ox_id); - - fr = fcoe_dev_from_skb(skb); - fr->fr_dev = lp; - fr->ptype = ptype; - cpu_idx = 0; -#ifdef CONFIG_SMP - /* - * The incoming frame exchange id(oxid) is ANDed with num of online - * cpu bits to get cpu_idx and then this cpu_idx is used for selecting - * a per cpu kernel thread from fcoe_percpu. In case the cpu is - * offline or no kernel thread for derived cpu_idx then cpu_idx is - * initialize to first online cpu index. - */ - cpu_idx = oxid & (num_online_cpus() - 1); - if (!fcoe_percpu[cpu_idx] || !cpu_online(cpu_idx)) - cpu_idx = first_cpu(cpu_online_map); -#endif - fps = fcoe_percpu[cpu_idx]; - - spin_lock_bh(&fps->fcoe_rx_list.lock); - __skb_queue_tail(&fps->fcoe_rx_list, skb); - if (fps->fcoe_rx_list.qlen == 1) - wake_up_process(fps->thread); - - spin_unlock_bh(&fps->fcoe_rx_list.lock); - - return 0; -err: -#ifdef CONFIG_SMP - stats = lp->dev_stats[smp_processor_id()]; -#else - stats = lp->dev_stats[0]; -#endif - if (stats) - stats->ErrorFrames++; - -err2: - kfree_skb(skb); - return -1; -} -EXPORT_SYMBOL_GPL(fcoe_rcv); - -/** - * fcoe_start_io - pass to netdev to start xmit for fcoe - * @skb: the skb to be xmitted - * - * Returns: 0 for success - **/ -static inline int fcoe_start_io(struct sk_buff *skb) -{ - int rc; - - skb_get(skb); - rc = dev_queue_xmit(skb); - if (rc != 0) - return rc; - kfree_skb(skb); - return 0; -} - -/** - * fcoe_get_paged_crc_eof - in case we need alloc a page for crc_eof - * @skb: the skb to be xmitted - * @tlen: total len - * - * Returns: 0 for success - **/ -static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) -{ - struct fcoe_percpu_s *fps; - struct page *page; - int cpu_idx; - - cpu_idx = get_cpu(); - fps = fcoe_percpu[cpu_idx]; - page = fps->crc_eof_page; - if (!page) { - page = alloc_page(GFP_ATOMIC); - if (!page) { - put_cpu(); - return -ENOMEM; - } - fps->crc_eof_page = page; - WARN_ON(fps->crc_eof_offset != 0); - } - - get_page(page); - skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, - fps->crc_eof_offset, tlen); - skb->len += tlen; - skb->data_len += tlen; - skb->truesize += tlen; - fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); - - if (fps->crc_eof_offset >= PAGE_SIZE) { - fps->crc_eof_page = NULL; - fps->crc_eof_offset = 0; - put_page(page); - } - put_cpu(); - return 0; -} - -/** - * fcoe_fc_crc - calculates FC CRC in this fcoe skb - * @fp: the fc_frame containg data to be checksummed - * - * This uses crc32() to calculate the crc for fc frame - * Return : 32 bit crc - * - **/ -u32 fcoe_fc_crc(struct fc_frame *fp) -{ - struct sk_buff *skb = fp_skb(fp); - struct skb_frag_struct *frag; - unsigned char *data; - unsigned long off, len, clen; - u32 crc; - unsigned i; - - crc = crc32(~0, skb->data, skb_headlen(skb)); - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - frag = &skb_shinfo(skb)->frags[i]; - off = frag->page_offset; - len = frag->size; - while (len > 0) { - clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); - data = kmap_atomic(frag->page + (off >> PAGE_SHIFT), - KM_SKB_DATA_SOFTIRQ); - crc = crc32(crc, data + (off & ~PAGE_MASK), clen); - kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); - off += clen; - len -= clen; - } - } - return crc; -} -EXPORT_SYMBOL_GPL(fcoe_fc_crc); - -/** - * fcoe_xmit - FCoE frame transmit function - * @lp: the associated local port - * @fp: the fc_frame to be transmitted - * - * Return : 0 for success - * - **/ -int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) -{ - int wlen, rc = 0; - u32 crc; - struct ethhdr *eh; - struct fcoe_crc_eof *cp; - struct sk_buff *skb; - struct fcoe_dev_stats *stats; - struct fc_frame_header *fh; - unsigned int hlen; /* header length implies the version */ - unsigned int tlen; /* trailer length */ - unsigned int elen; /* eth header, may include vlan */ - int flogi_in_progress = 0; - struct fcoe_softc *fc; - u8 sof, eof; - struct fcoe_hdr *hp; - - WARN_ON((fr_len(fp) % sizeof(u32)) != 0); - - fc = fcoe_softc(lp); - /* - * if it is a flogi then we need to learn gw-addr - * and my own fcid - */ - fh = fc_frame_header_get(fp); - if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { - if (fc_frame_payload_op(fp) == ELS_FLOGI) { - fc->flogi_oxid = ntohs(fh->fh_ox_id); - fc->address_mode = FCOE_FCOUI_ADDR_MODE; - fc->flogi_progress = 1; - flogi_in_progress = 1; - } else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) { - /* - * Here we must've gotten an SID by accepting an FLOGI - * from a point-to-point connection. Switch to using - * the source mac based on the SID. The destination - * MAC in this case would have been set by receving the - * FLOGI. - */ - fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id); - fc->flogi_progress = 0; - } - } - - skb = fp_skb(fp); - sof = fr_sof(fp); - eof = fr_eof(fp); - - elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ? - sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr); - hlen = sizeof(struct fcoe_hdr); - tlen = sizeof(struct fcoe_crc_eof); - wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; - - /* crc offload */ - if (likely(lp->crc_offload)) { - skb->ip_summed = CHECKSUM_COMPLETE; - skb->csum_start = skb_headroom(skb); - skb->csum_offset = skb->len; - crc = 0; - } else { - skb->ip_summed = CHECKSUM_NONE; - crc = fcoe_fc_crc(fp); - } - - /* copy fc crc and eof to the skb buff */ - if (skb_is_nonlinear(skb)) { - skb_frag_t *frag; - if (fcoe_get_paged_crc_eof(skb, tlen)) { - kfree(skb); - return -ENOMEM; - } - frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; - cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ) - + frag->page_offset; - } else { - cp = (struct fcoe_crc_eof *)skb_put(skb, tlen); - } - - memset(cp, 0, sizeof(*cp)); - cp->fcoe_eof = eof; - cp->fcoe_crc32 = cpu_to_le32(~crc); - - if (skb_is_nonlinear(skb)) { - kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ); - cp = NULL; - } - - /* adjust skb netowrk/transport offsets to match mac/fcoe/fc */ - skb_push(skb, elen + hlen); - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - skb->mac_len = elen; - skb->protocol = htons(ETH_P_802_3); - skb->dev = fc->real_dev; - - /* fill up mac and fcoe headers */ - eh = eth_hdr(skb); - eh->h_proto = htons(ETH_P_FCOE); - if (fc->address_mode == FCOE_FCOUI_ADDR_MODE) - fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); - else - /* insert GW address */ - memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN); - - if (unlikely(flogi_in_progress)) - memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN); - else - memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN); - - hp = (struct fcoe_hdr *)(eh + 1); - memset(hp, 0, sizeof(*hp)); - if (FC_FCOE_VER) - FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); - hp->fcoe_sof = sof; - - /* update tx stats: regardless if LLD fails */ - stats = lp->dev_stats[smp_processor_id()]; - if (stats) { - stats->TxFrames++; - stats->TxWords += wlen; - } - - /* send down to lld */ - fr_dev(fp) = lp; - if (fc->fcoe_pending_queue.qlen) - rc = fcoe_check_wait_queue(lp); - - if (rc == 0) - rc = fcoe_start_io(skb); - - if (rc) { - fcoe_insert_wait_queue(lp, skb); - if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) - fc_pause(lp); - } - - return 0; -} -EXPORT_SYMBOL_GPL(fcoe_xmit); - -/* - * fcoe_percpu_receive_thread - recv thread per cpu - * @arg: ptr to the fcoe per cpu struct - * - * Return: 0 for success - * - */ -int fcoe_percpu_receive_thread(void *arg) -{ - struct fcoe_percpu_s *p = arg; - u32 fr_len; - struct fc_lport *lp; - struct fcoe_rcv_info *fr; - struct fcoe_dev_stats *stats; - struct fc_frame_header *fh; - struct sk_buff *skb; - struct fcoe_crc_eof crc_eof; - struct fc_frame *fp; - u8 *mac = NULL; - struct fcoe_softc *fc; - struct fcoe_hdr *hp; - - set_user_nice(current, 19); - - while (!kthread_should_stop()) { - - spin_lock_bh(&p->fcoe_rx_list.lock); - while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) { - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_bh(&p->fcoe_rx_list.lock); - schedule(); - set_current_state(TASK_RUNNING); - if (kthread_should_stop()) - return 0; - spin_lock_bh(&p->fcoe_rx_list.lock); - } - spin_unlock_bh(&p->fcoe_rx_list.lock); - fr = fcoe_dev_from_skb(skb); - lp = fr->fr_dev; - if (unlikely(lp == NULL)) { - FC_DBG("invalid HBA Structure"); - kfree_skb(skb); - continue; - } - - stats = lp->dev_stats[smp_processor_id()]; - - if (unlikely(debug_fcoe)) { - FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p " - "tail:%p end:%p sum:%d dev:%s", - skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : "<NULL>"); - } - - /* - * Save source MAC address before discarding header. - */ - fc = lport_priv(lp); - if (unlikely(fc->flogi_progress)) - mac = eth_hdr(skb)->h_source; - - if (skb_is_nonlinear(skb)) - skb_linearize(skb); /* not ideal */ - - /* - * Frame length checks and setting up the header pointers - * was done in fcoe_rcv already. - */ - hp = (struct fcoe_hdr *) skb_network_header(skb); - fh = (struct fc_frame_header *) skb_transport_header(skb); - - if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { - if (stats) { - if (stats->ErrorFrames < 5) - FC_DBG("unknown FCoE version %x", - FC_FCOE_DECAPS_VER(hp)); - stats->ErrorFrames++; - } - kfree_skb(skb); - continue; - } - - skb_pull(skb, sizeof(struct fcoe_hdr)); - fr_len = skb->len - sizeof(struct fcoe_crc_eof); - - if (stats) { - stats->RxFrames++; - stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; - } - - fp = (struct fc_frame *)skb; - fc_frame_init(fp); - fr_dev(fp) = lp; - fr_sof(fp) = hp->fcoe_sof; - - /* Copy out the CRC and EOF trailer for access */ - if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { - kfree_skb(skb); - continue; - } - fr_eof(fp) = crc_eof.fcoe_eof; - fr_crc(fp) = crc_eof.fcoe_crc32; - if (pskb_trim(skb, fr_len)) { - kfree_skb(skb); - continue; - } - - /* - * We only check CRC if no offload is available and if it is - * it's solicited data, in which case, the FCP layer would - * check it during the copy. - */ - if (lp->crc_offload) - fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; - else - fr_flags(fp) |= FCPHF_CRC_UNCHECKED; - - fh = fc_frame_header_get(fp); - if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && - fh->fh_type == FC_TYPE_FCP) { - fc_exch_recv(lp, lp->emp, fp); - continue; - } - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { - if (le32_to_cpu(fr_crc(fp)) != - ~crc32(~0, skb->data, fr_len)) { - if (debug_fcoe || stats->InvalidCRCCount < 5) - printk(KERN_WARNING "fcoe: dropping " - "frame with CRC error\n"); - stats->InvalidCRCCount++; - stats->ErrorFrames++; - fc_frame_free(fp); - continue; - } - fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; - } - /* non flogi and non data exchanges are handled here */ - if (unlikely(fc->flogi_progress)) - fcoe_recv_flogi(fc, fp, mac); - fc_exch_recv(lp, lp->emp, fp); - } - return 0; -} - -/** - * fcoe_recv_flogi - flogi receive function - * @fc: associated fcoe_softc - * @fp: the recieved frame - * @sa: the source address of this flogi - * - * This is responsible to parse the flogi response and sets the corresponding - * mac address for the initiator, eitehr OUI based or GW based. - * - * Returns: none - **/ -static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa) -{ - struct fc_frame_header *fh; - u8 op; - - fh = fc_frame_header_get(fp); - if (fh->fh_type != FC_TYPE_ELS) - return; - op = fc_frame_payload_op(fp); - if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP && - fc->flogi_oxid == ntohs(fh->fh_ox_id)) { - /* - * FLOGI accepted. - * If the src mac addr is FC_OUI-based, then we mark the - * address_mode flag to use FC_OUI-based Ethernet DA. - * Otherwise we use the FCoE gateway addr - */ - if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) { - fc->address_mode = FCOE_FCOUI_ADDR_MODE; - } else { - memcpy(fc->dest_addr, sa, ETH_ALEN); - fc->address_mode = FCOE_GW_ADDR_MODE; - } - - /* - * Remove any previously-set unicast MAC filter. - * Add secondary FCoE MAC address filter for our OUI. - */ - rtnl_lock(); - if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 })) - dev_unicast_delete(fc->real_dev, fc->data_src_addr, - ETH_ALEN); - fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id); - dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN); - rtnl_unlock(); - - fc->flogi_progress = 0; - } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) { - /* - * Save source MAC for point-to-point responses. - */ - memcpy(fc->dest_addr, sa, ETH_ALEN); - fc->address_mode = FCOE_GW_ADDR_MODE; - } -} - -/** - * fcoe_watchdog - fcoe timer callback - * @vp: - * - * This checks the pending queue length for fcoe and put fcoe to be paused state - * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the - * fcoe_hostlist. - * - * Returns: 0 for success - **/ -void fcoe_watchdog(ulong vp) -{ - struct fc_lport *lp; - struct fcoe_softc *fc; - int paused = 0; - - read_lock(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - lp = fc->lp; - if (lp) { - if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) - paused = 1; - if (fcoe_check_wait_queue(lp) < FCOE_MAX_QUEUE_DEPTH) { - if (paused) - fc_unpause(lp); - } - } - } - read_unlock(&fcoe_hostlist_lock); - - fcoe_timer.expires = jiffies + (1 * HZ); - add_timer(&fcoe_timer); -} - - -/** - * fcoe_check_wait_queue - put the skb into fcoe pending xmit queue - * @lp: the fc_port for this skb - * @skb: the associated skb to be xmitted - * - * This empties the wait_queue, dequeue the head of the wait_queue queue - * and calls fcoe_start_io() for each packet, if all skb have been - * transmitted, return 0 if a error occurs, then restore wait_queue and - * try again later. - * - * The wait_queue is used when the skb transmit fails. skb will go - * in the wait_queue which will be emptied by the time function OR - * by the next skb transmit. - * - * Returns: 0 for success - **/ -static int fcoe_check_wait_queue(struct fc_lport *lp) -{ - int rc, unpause = 0; - int paused = 0; - struct sk_buff *skb; - struct fcoe_softc *fc; - - fc = fcoe_softc(lp); - spin_lock_bh(&fc->fcoe_pending_queue.lock); - - /* - * is this interface paused? - */ - if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) - paused = 1; - if (fc->fcoe_pending_queue.qlen) { - while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { - spin_unlock_bh(&fc->fcoe_pending_queue.lock); - rc = fcoe_start_io(skb); - if (rc) { - fcoe_insert_wait_queue_head(lp, skb); - return rc; - } - spin_lock_bh(&fc->fcoe_pending_queue.lock); - } - if (fc->fcoe_pending_queue.qlen < FCOE_MAX_QUEUE_DEPTH) - unpause = 1; - } - spin_unlock_bh(&fc->fcoe_pending_queue.lock); - if ((unpause) && (paused)) - fc_unpause(lp); - return fc->fcoe_pending_queue.qlen; -} - -/** - * fcoe_insert_wait_queue_head - puts skb to fcoe pending queue head - * @lp: the fc_port for this skb - * @skb: the associated skb to be xmitted - * - * Returns: none - **/ -static void fcoe_insert_wait_queue_head(struct fc_lport *lp, - struct sk_buff *skb) -{ - struct fcoe_softc *fc; - - fc = fcoe_softc(lp); - spin_lock_bh(&fc->fcoe_pending_queue.lock); - __skb_queue_head(&fc->fcoe_pending_queue, skb); - spin_unlock_bh(&fc->fcoe_pending_queue.lock); -} - -/** - * fcoe_insert_wait_queue - put the skb into fcoe pending queue tail - * @lp: the fc_port for this skb - * @skb: the associated skb to be xmitted - * - * Returns: none - **/ -static void fcoe_insert_wait_queue(struct fc_lport *lp, - struct sk_buff *skb) -{ - struct fcoe_softc *fc; - - fc = fcoe_softc(lp); - spin_lock_bh(&fc->fcoe_pending_queue.lock); - __skb_queue_tail(&fc->fcoe_pending_queue, skb); - spin_unlock_bh(&fc->fcoe_pending_queue.lock); -} - -/** - * fcoe_dev_setup - setup link change notification interface - * - **/ -static void fcoe_dev_setup(void) -{ - /* - * here setup a interface specific wd time to - * monitor the link state - */ - register_netdevice_notifier(&fcoe_notifier); -} - -/** - * fcoe_dev_setup - cleanup link change notification interface - **/ -static void fcoe_dev_cleanup(void) -{ - unregister_netdevice_notifier(&fcoe_notifier); -} - -/** - * fcoe_device_notification - netdev event notification callback - * @notifier: context of the notification - * @event: type of event - * @ptr: fixed array for output parsed ifname - * - * This function is called by the ethernet driver in case of link change event - * - * Returns: 0 for success - **/ -static int fcoe_device_notification(struct notifier_block *notifier, - ulong event, void *ptr) -{ - struct fc_lport *lp = NULL; - struct net_device *real_dev = ptr; - struct fcoe_softc *fc; - struct fcoe_dev_stats *stats; - u16 new_status; - u32 mfs; - int rc = NOTIFY_OK; - - read_lock(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == real_dev) { - lp = fc->lp; - break; - } - } - read_unlock(&fcoe_hostlist_lock); - if (lp == NULL) { - rc = NOTIFY_DONE; - goto out; - } - - new_status = lp->link_status; - switch (event) { - case NETDEV_DOWN: - case NETDEV_GOING_DOWN: - new_status &= ~FC_LINK_UP; - break; - case NETDEV_UP: - case NETDEV_CHANGE: - new_status &= ~FC_LINK_UP; - if (!fcoe_link_ok(lp)) - new_status |= FC_LINK_UP; - break; - case NETDEV_CHANGEMTU: - mfs = fc->real_dev->mtu - - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); - if (mfs >= FC_MIN_MAX_FRAME) - fc_set_mfs(lp, mfs); - new_status &= ~FC_LINK_UP; - if (!fcoe_link_ok(lp)) - new_status |= FC_LINK_UP; - break; - case NETDEV_REGISTER: - break; - default: - FC_DBG("unknown event %ld call", event); - } - if (lp->link_status != new_status) { - if ((new_status & FC_LINK_UP) == FC_LINK_UP) - fc_linkup(lp); - else { - stats = lp->dev_stats[smp_processor_id()]; - if (stats) - stats->LinkFailureCount++; - fc_linkdown(lp); - fcoe_clean_pending_queue(lp); - } - } -out: - return rc; -} - -/** - * fcoe_if_to_netdev - parse a name buffer to get netdev - * @ifname: fixed array for output parsed ifname - * @buffer: incoming buffer to be copied - * - * Returns: NULL or ptr to netdeive - **/ -static struct net_device *fcoe_if_to_netdev(const char *buffer) -{ - char *cp; - char ifname[IFNAMSIZ + 2]; - - if (buffer) { - strlcpy(ifname, buffer, IFNAMSIZ); - cp = ifname + strlen(ifname); - while (--cp >= ifname && *cp == '\n') - *cp = '\0'; - return dev_get_by_name(&init_net, ifname); - } - return NULL; -} - -/** - * fcoe_netdev_to_module_owner - finds out the nic drive moddule of the netdev - * @netdev: the target netdev - * - * Returns: ptr to the struct module, NULL for failure - **/ -static struct module *fcoe_netdev_to_module_owner( - const struct net_device *netdev) -{ - struct device *dev; - - if (!netdev) - return NULL; - - dev = netdev->dev.parent; - if (!dev) - return NULL; - - if (!dev->driver) - return NULL; - - return dev->driver->owner; -} - -/** - * fcoe_ethdrv_get - holds the nic driver module by try_module_get() for - * the corresponding netdev. - * @netdev: the target netdev - * - * Returns: 0 for succsss - **/ -static int fcoe_ethdrv_get(const struct net_device *netdev) -{ - struct module *owner; - - owner = fcoe_netdev_to_module_owner(netdev); - if (owner) { - printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n", - module_name(owner), netdev->name); - return try_module_get(owner); - } - return -ENODEV; -} - -/** - * fcoe_ethdrv_get - releases the nic driver module by module_put for - * the corresponding netdev. - * @netdev: the target netdev - * - * Returns: 0 for succsss - **/ -static int fcoe_ethdrv_put(const struct net_device *netdev) -{ - struct module *owner; - - owner = fcoe_netdev_to_module_owner(netdev); - if (owner) { - printk(KERN_DEBUG "fcoe:release driver module %s for %s\n", - module_name(owner), netdev->name); - module_put(owner); - return 0; - } - return -ENODEV; -} - -/** - * fcoe_destroy- handles the destroy from sysfs - * @buffer: expcted to be a eth if name - * @kp: associated kernel param - * - * Returns: 0 for success - **/ -static int fcoe_destroy(const char *buffer, struct kernel_param *kp) -{ - int rc; - struct net_device *netdev; - - netdev = fcoe_if_to_netdev(buffer); - if (!netdev) { - rc = -ENODEV; - goto out_nodev; - } - /* look for existing lport */ - if (!fcoe_hostlist_lookup(netdev)) { - rc = -ENODEV; - goto out_putdev; - } - /* pass to transport */ - rc = fcoe_transport_release(netdev); - if (rc) { - printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n", - netdev->name); - rc = -EIO; - goto out_putdev; - } - fcoe_ethdrv_put(netdev); - rc = 0; -out_putdev: - dev_put(netdev); -out_nodev: - return rc; -} - -/** - * fcoe_create - handles the create call from sysfs - * @buffer: expcted to be a eth if name - * @kp: associated kernel param - * - * Returns: 0 for success - **/ -static int fcoe_create(const char *buffer, struct kernel_param *kp) -{ - int rc; - struct net_device *netdev; - - netdev = fcoe_if_to_netdev(buffer); - if (!netdev) { - rc = -ENODEV; - goto out_nodev; - } - /* look for existing lport */ - if (fcoe_hostlist_lookup(netdev)) { - rc = -EEXIST; - goto out_putdev; - } - fcoe_ethdrv_get(netdev); - - /* pass to transport */ - rc = fcoe_transport_attach(netdev); - if (rc) { - printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n", - netdev->name); - fcoe_ethdrv_put(netdev); - rc = -EIO; - goto out_putdev; - } - rc = 0; -out_putdev: - dev_put(netdev); -out_nodev: - return rc; -} - -module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(create, "string"); -MODULE_PARM_DESC(create, "Create fcoe port using net device passed in."); -module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(destroy, "string"); -MODULE_PARM_DESC(destroy, "Destroy fcoe port"); - -/* - * fcoe_link_ok - check if link is ok for the fc_lport - * @lp: ptr to the fc_lport - * - * Any permanently-disqualifying conditions have been previously checked. - * This also updates the speed setting, which may change with link for 100/1000. - * - * This function should probably be checking for PAUSE support at some point - * in the future. Currently Per-priority-pause is not determinable using - * ethtool, so we shouldn't be restrictive until that problem is resolved. - * - * Returns: 0 if link is OK for use by FCoE. - * - */ -int fcoe_link_ok(struct fc_lport *lp) -{ - struct fcoe_softc *fc = fcoe_softc(lp); - struct net_device *dev = fc->real_dev; - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - int rc = 0; - - if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) { - dev = fc->phys_dev; - if (dev->ethtool_ops->get_settings) { - dev->ethtool_ops->get_settings(dev, &ecmd); - lp->link_supported_speeds &= - ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); - if (ecmd.supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) - lp->link_supported_speeds |= FC_PORTSPEED_1GBIT; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lp->link_supported_speeds |= - FC_PORTSPEED_10GBIT; - if (ecmd.speed == SPEED_1000) - lp->link_speed = FC_PORTSPEED_1GBIT; - if (ecmd.speed == SPEED_10000) - lp->link_speed = FC_PORTSPEED_10GBIT; - } - } else - rc = -1; - - return rc; -} -EXPORT_SYMBOL_GPL(fcoe_link_ok); - -/* - * fcoe_percpu_clean - frees skb of the corresponding lport from the per - * cpu queue. - * @lp: the fc_lport - */ -void fcoe_percpu_clean(struct fc_lport *lp) -{ - int idx; - struct fcoe_percpu_s *pp; - struct fcoe_rcv_info *fr; - struct sk_buff_head *list; - struct sk_buff *skb, *next; - struct sk_buff *head; - - for (idx = 0; idx < NR_CPUS; idx++) { - if (fcoe_percpu[idx]) { - pp = fcoe_percpu[idx]; - spin_lock_bh(&pp->fcoe_rx_list.lock); - list = &pp->fcoe_rx_list; - head = list->next; - for (skb = head; skb != (struct sk_buff *)list; - skb = next) { - next = skb->next; - fr = fcoe_dev_from_skb(skb); - if (fr->fr_dev == lp) { - __skb_unlink(skb, list); - kfree_skb(skb); - } - } - spin_unlock_bh(&pp->fcoe_rx_list.lock); - } - } -} -EXPORT_SYMBOL_GPL(fcoe_percpu_clean); - -/** - * fcoe_clean_pending_queue - dequeue skb and free it - * @lp: the corresponding fc_lport - * - * Returns: none - **/ -void fcoe_clean_pending_queue(struct fc_lport *lp) -{ - struct fcoe_softc *fc = lport_priv(lp); - struct sk_buff *skb; - - spin_lock_bh(&fc->fcoe_pending_queue.lock); - while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { - spin_unlock_bh(&fc->fcoe_pending_queue.lock); - kfree_skb(skb); - spin_lock_bh(&fc->fcoe_pending_queue.lock); - } - spin_unlock_bh(&fc->fcoe_pending_queue.lock); -} -EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); - -/** - * libfc_host_alloc - allocate a Scsi_Host with room for the fc_lport - * @sht: ptr to the scsi host templ - * @priv_size: size of private data after fc_lport - * - * Returns: ptr to Scsi_Host - * TODO - to libfc? - */ -static inline struct Scsi_Host *libfc_host_alloc( - struct scsi_host_template *sht, int priv_size) -{ - return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size); -} - -/** - * fcoe_host_alloc - allocate a Scsi_Host with room for the fcoe_softc - * @sht: ptr to the scsi host templ - * @priv_size: size of private data after fc_lport - * - * Returns: ptr to Scsi_Host - */ -struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size) -{ - return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size); -} -EXPORT_SYMBOL_GPL(fcoe_host_alloc); - -/* - * fcoe_reset - resets the fcoe - * @shost: shost the reset is from - * - * Returns: always 0 - */ -int fcoe_reset(struct Scsi_Host *shost) -{ - struct fc_lport *lport = shost_priv(shost); - fc_lport_reset(lport); - return 0; -} -EXPORT_SYMBOL_GPL(fcoe_reset); - -/* - * fcoe_wwn_from_mac - converts 48-bit IEEE MAC address to 64-bit FC WWN. - * @mac: mac address - * @scheme: check port - * @port: port indicator for converting - * - * Returns: u64 fc world wide name - */ -u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], - unsigned int scheme, unsigned int port) -{ - u64 wwn; - u64 host_mac; - - /* The MAC is in NO, so flip only the low 48 bits */ - host_mac = ((u64) mac[0] << 40) | - ((u64) mac[1] << 32) | - ((u64) mac[2] << 24) | - ((u64) mac[3] << 16) | - ((u64) mac[4] << 8) | - (u64) mac[5]; - - WARN_ON(host_mac >= (1ULL << 48)); - wwn = host_mac | ((u64) scheme << 60); - switch (scheme) { - case 1: - WARN_ON(port != 0); - break; - case 2: - WARN_ON(port >= 0xfff); - wwn |= (u64) port << 48; - break; - default: - WARN_ON(1); - break; - } - - return wwn; -} -EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac); -/* - * fcoe_hostlist_lookup_softc - find the corresponding lport by a given device - * @device: this is currently ptr to net_device - * - * Returns: NULL or the located fcoe_softc - */ -static struct fcoe_softc *fcoe_hostlist_lookup_softc( - const struct net_device *dev) -{ - struct fcoe_softc *fc; - - read_lock(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == dev) { - read_unlock(&fcoe_hostlist_lock); - return fc; - } - } - read_unlock(&fcoe_hostlist_lock); - return NULL; -} - -/* - * fcoe_hostlist_lookup - find the corresponding lport by netdev - * @netdev: ptr to net_device - * - * Returns: 0 for success - */ -struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) -{ - struct fcoe_softc *fc; - - fc = fcoe_hostlist_lookup_softc(netdev); - - return (fc) ? fc->lp : NULL; -} -EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup); - -/* - * fcoe_hostlist_add - add a lport to lports list - * @lp: ptr to the fc_lport to badded - * - * Returns: 0 for success - */ -int fcoe_hostlist_add(const struct fc_lport *lp) -{ - struct fcoe_softc *fc; - - fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); - if (!fc) { - fc = fcoe_softc(lp); - write_lock_bh(&fcoe_hostlist_lock); - list_add_tail(&fc->list, &fcoe_hostlist); - write_unlock_bh(&fcoe_hostlist_lock); - } - return 0; -} -EXPORT_SYMBOL_GPL(fcoe_hostlist_add); - -/* - * fcoe_hostlist_remove - remove a lport from lports list - * @lp: ptr to the fc_lport to badded - * - * Returns: 0 for success - */ -int fcoe_hostlist_remove(const struct fc_lport *lp) -{ - struct fcoe_softc *fc; - - fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); - BUG_ON(!fc); - write_lock_bh(&fcoe_hostlist_lock); - list_del(&fc->list); - write_unlock_bh(&fcoe_hostlist_lock); - - return 0; -} -EXPORT_SYMBOL_GPL(fcoe_hostlist_remove); - -/** - * fcoe_libfc_config - sets up libfc related properties for lport - * @lp: ptr to the fc_lport - * @tt: libfc function template - * - * Returns : 0 for success - **/ -int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt) -{ - /* Set the function pointers set by the LLDD */ - memcpy(&lp->tt, tt, sizeof(*tt)); - if (fc_fcp_init(lp)) - return -ENOMEM; - fc_exch_init(lp); - fc_elsct_init(lp); - fc_lport_init(lp); - fc_rport_init(lp); - fc_disc_init(lp); - - return 0; -} -EXPORT_SYMBOL_GPL(fcoe_libfc_config); - -/** - * fcoe_init - fcoe module loading initialization - * - * Initialization routine - * 1. Will create fc transport software structure - * 2. initialize the link list of port information structure - * - * Returns 0 on success, negative on failure - **/ -static int __init fcoe_init(void) -{ - int cpu; - struct fcoe_percpu_s *p; - - - INIT_LIST_HEAD(&fcoe_hostlist); - rwlock_init(&fcoe_hostlist_lock); - -#ifdef CONFIG_HOTPLUG_CPU - register_cpu_notifier(&fcoe_cpu_notifier); -#endif /* CONFIG_HOTPLUG_CPU */ - - /* - * initialize per CPU interrupt thread - */ - for_each_online_cpu(cpu) { - p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL); - if (p) { - p->thread = kthread_create(fcoe_percpu_receive_thread, - (void *)p, - "fcoethread/%d", cpu); - - /* - * if there is no error then bind the thread to the cpu - * initialize the semaphore and skb queue head - */ - if (likely(!IS_ERR(p->thread))) { - p->cpu = cpu; - fcoe_percpu[cpu] = p; - skb_queue_head_init(&p->fcoe_rx_list); - kthread_bind(p->thread, cpu); - wake_up_process(p->thread); - } else { - fcoe_percpu[cpu] = NULL; - kfree(p); - - } - } - } - - /* - * setup link change notification - */ - fcoe_dev_setup(); - - init_timer(&fcoe_timer); - fcoe_timer.data = 0; - fcoe_timer.function = fcoe_watchdog; - fcoe_timer.expires = (jiffies + (10 * HZ)); - add_timer(&fcoe_timer); - - /* initiatlize the fcoe transport */ - fcoe_transport_init(); - - fcoe_sw_init(); - - return 0; -} -module_init(fcoe_init); - -/** - * fcoe_exit - fcoe module unloading cleanup - * - * Returns 0 on success, negative on failure - **/ -static void __exit fcoe_exit(void) -{ - u32 idx; - struct fcoe_softc *fc, *tmp; - struct fcoe_percpu_s *p; - struct sk_buff *skb; - - /* - * Stop all call back interfaces - */ -#ifdef CONFIG_HOTPLUG_CPU - unregister_cpu_notifier(&fcoe_cpu_notifier); -#endif /* CONFIG_HOTPLUG_CPU */ - fcoe_dev_cleanup(); - - /* - * stop timer - */ - del_timer_sync(&fcoe_timer); - - /* releases the assocaited fcoe transport for each lport */ - list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list) - fcoe_transport_release(fc->real_dev); - - for (idx = 0; idx < NR_CPUS; idx++) { - if (fcoe_percpu[idx]) { - kthread_stop(fcoe_percpu[idx]->thread); - p = fcoe_percpu[idx]; - spin_lock_bh(&p->fcoe_rx_list.lock); - while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL) - kfree_skb(skb); - spin_unlock_bh(&p->fcoe_rx_list.lock); - if (fcoe_percpu[idx]->crc_eof_page) - put_page(fcoe_percpu[idx]->crc_eof_page); - kfree(fcoe_percpu[idx]); - } - } - - /* remove sw trasnport */ - fcoe_sw_exit(); - - /* detach the transport */ - fcoe_transport_exit(); -} -module_exit(fcoe_exit); |
