Merge tag 'fcoe-3.13' into for-linus
authorJames Bottomley <JBottomley@Parallels.com>
Sun, 10 Nov 2013 04:19:15 +0000 (12:19 +0800)
committerJames Bottomley <JBottomley@Parallels.com>
Sun, 10 Nov 2013 04:19:15 +0000 (12:19 +0800)
Pull Request for 3.13 for FCOE tree.

Signed-off-by: James Bottomley <JBottomley@Parallels.com>
12 files changed:
drivers/scsi/bnx2fc/bnx2fc_fcoe.c
drivers/scsi/fcoe/fcoe.c
drivers/scsi/fcoe/fcoe_ctlr.c
drivers/scsi/fcoe/fcoe_sysfs.c
drivers/scsi/fnic/fnic_fcs.c
drivers/scsi/libfc/fc_exch.c
drivers/scsi/libfc/fc_fcp.c
drivers/scsi/libfc/fc_lport.c
drivers/scsi/libfc/fc_rport.c
include/scsi/fc/fc_fc2.h
include/scsi/libfc.h
include/scsi/libfcoe.h

index 3c204ad..9b94850 100644 (file)
@@ -542,8 +542,7 @@ static void bnx2fc_recv_frame(struct sk_buff *skb)
        vn_port = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
        if (vn_port) {
                port = lport_priv(vn_port);
-               if (compare_ether_addr(port->data_src_addr, dest_mac)
-                   != 0) {
+               if (!ether_addr_equal(port->data_src_addr, dest_mac)) {
                        BNX2FC_HBA_DBG(lport, "fpma mismatch\n");
                        put_cpu();
                        kfree_skb(skb);
@@ -1381,6 +1380,7 @@ struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba,
                return NULL;
        }
        ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       ctlr->cdev = ctlr_dev;
        interface = fcoe_ctlr_priv(ctlr);
        dev_hold(netdev);
        kref_init(&interface->kref);
index 07453bb..f317000 100644 (file)
@@ -408,6 +408,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
        }
 
        ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       ctlr->cdev = ctlr_dev;
        fcoe = fcoe_ctlr_priv(ctlr);
 
        dev_hold(netdev);
@@ -1440,22 +1441,28 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
        ctlr = fcoe_to_ctlr(fcoe);
        lport = ctlr->lp;
        if (unlikely(!lport)) {
-               FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
+               FCOE_NETDEV_DBG(netdev, "Cannot find hba structure\n");
                goto err2;
        }
        if (!lport->link_up)
                goto err2;
 
-       FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p "
-                       "data:%p tail:%p end:%p sum:%d dev:%s",
+       FCOE_NETDEV_DBG(netdev,
+                       "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",
                        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>");
 
+
+       skb = skb_share_check(skb, GFP_ATOMIC);
+
+       if (skb == NULL)
+               return NET_RX_DROP;
+
        eh = eth_hdr(skb);
 
        if (is_fip_mode(ctlr) &&
-           compare_ether_addr(eh->h_source, ctlr->dest_addr)) {
+           !ether_addr_equal(eh->h_source, ctlr->dest_addr)) {
                FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",
                                eh->h_source);
                goto err;
@@ -1540,13 +1547,13 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
                wake_up_process(fps->thread);
        spin_unlock(&fps->fcoe_rx_list.lock);
 
-       return 0;
+       return NET_RX_SUCCESS;
 err:
        per_cpu_ptr(lport->stats, get_cpu())->ErrorFrames++;
        put_cpu();
 err2:
        kfree_skb(skb);
-       return -1;
+       return NET_RX_DROP;
 }
 
 /**
@@ -1788,13 +1795,13 @@ static void fcoe_recv_frame(struct sk_buff *skb)
        lport = fr->fr_dev;
        if (unlikely(!lport)) {
                if (skb->destructor != fcoe_percpu_flush_done)
-                       FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
+                       FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb\n");
                kfree_skb(skb);
                return;
        }
 
-       FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
-                       "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
+       FCOE_NETDEV_DBG(skb->dev,
+                       "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",
                        skb->len, skb->data_len,
                        skb->head, skb->data, skb_tail_pointer(skb),
                        skb_end_pointer(skb), skb->csum,
index 203415e..34a1b1f 100644 (file)
@@ -160,74 +160,113 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
 }
 EXPORT_SYMBOL(fcoe_ctlr_init);
 
+/**
+ * fcoe_sysfs_fcf_add() - Add a fcoe_fcf{,_device} to a fcoe_ctlr{,_device}
+ * @new: The newly discovered FCF
+ *
+ * Called with fip->ctlr_mutex held
+ */
 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;
+       struct fcoe_ctlr_device *ctlr_dev;
+       struct fcoe_fcf_device *temp, *fcf_dev;
+       int rc = -ENOMEM;
 
        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;
+       temp = kzalloc(sizeof(*temp), GFP_KERNEL);
+       if (!temp)
                goto out;
-       }
+
+       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 */
 
        /*
-        * 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.
+        * If ctlr_dev doesn't exist then it means we're a libfcoe user
+        * who doesn't use fcoe_syfs and didn't allocate a fcoe_ctlr_device.
+        * fnic would be an example of a driver with this behavior. In this
+        * case we want to add the fcoe_fcf to the fcoe_ctlr list, but we
+        * don't want to make sysfs changes.
         */
-       BUG_ON(fcf_dev->priv);
 
-       fcf_dev->priv = new;
-       new->fcf_dev = fcf_dev;
+       ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
+       if (ctlr_dev) {
+               mutex_lock(&ctlr_dev->lock);
+               fcf_dev = fcoe_fcf_device_add(ctlr_dev, temp);
+               if (unlikely(!fcf_dev)) {
+                       rc = -ENOMEM;
+                       mutex_unlock(&ctlr_dev->lock);
+                       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;
+               mutex_unlock(&ctlr_dev->lock);
+       }
 
        list_add(&new->list, &fip->fcfs);
        fip->fcf_count++;
+       rc = 0;
 
 out:
-       mutex_unlock(&ctlr_dev->lock);
+       kfree(temp);
        return rc;
 }
 
+/**
+ * fcoe_sysfs_fcf_del() - Remove a fcoe_fcf{,_device} to a fcoe_ctlr{,_device}
+ * @new: The FCF to be removed
+ *
+ * Called with fip->ctlr_mutex held
+ */
 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_ctlr_device *cdev;
        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);
+       /*
+        * If ctlr_dev doesn't exist then it means we're a libfcoe user
+        * who doesn't use fcoe_syfs and didn't allocate a fcoe_ctlr_device
+        * or a fcoe_fcf_device.
+        *
+        * fnic would be an example of a driver with this behavior. In this
+        * case we want to remove the fcoe_fcf from the fcoe_ctlr list (above),
+        * but we don't want to make sysfs changes.
+        */
+       cdev = fcoe_ctlr_to_ctlr_dev(fip);
+       if (cdev) {
+               mutex_lock(&cdev->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(&cdev->lock);
+       }
 }
 
 /**
@@ -300,7 +339,7 @@ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
        spin_unlock_bh(&fip->ctlr_lock);
        sel = fip->sel_fcf;
 
-       if (sel && !compare_ether_addr(sel->fcf_mac, fip->dest_addr))
+       if (sel && ether_addr_equal(sel->fcf_mac, fip->dest_addr))
                goto unlock;
        if (!is_zero_ether_addr(fip->dest_addr)) {
                printk(KERN_NOTICE "libfcoe: host%d: "
@@ -1000,7 +1039,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
                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) {
+                   ether_addr_equal(fcf->fcf_mac, new.fcf_mac)) {
                        found = 1;
                        break;
                }
@@ -1340,7 +1379,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
                        mp = (struct fip_mac_desc *)desc;
                        if (dlen < sizeof(*mp))
                                goto err;
-                       if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac))
+                       if (!ether_addr_equal(mp->fd_mac, fcf->fcf_mac))
                                goto err;
                        desc_mask &= ~BIT(FIP_DT_MAC);
                        break;
@@ -1418,8 +1457,8 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
                         * 'port_id' is already validated, check MAC address and
                         * wwpn
                         */
-                       if (compare_ether_addr(fip->get_src_addr(vn_port),
-                                               vp->fd_mac) != 0 ||
+                       if (!ether_addr_equal(fip->get_src_addr(vn_port),
+                                             vp->fd_mac) ||
                                get_unaligned_be64(&vp->fd_wwpn) !=
                                                        vn_port->wwpn)
                                continue;
@@ -1453,6 +1492,9 @@ err:
  */
 void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
+       skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!skb)
+               return;
        skb_queue_tail(&fip->fip_recv_list, skb);
        schedule_work(&fip->recv_work);
 }
@@ -1479,12 +1521,12 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
                goto drop;
        eh = eth_hdr(skb);
        if (fip->mode == FIP_MODE_VN2VN) {
-               if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
-                   compare_ether_addr(eh->h_dest, fcoe_all_vn2vn) &&
-                   compare_ether_addr(eh->h_dest, fcoe_all_p2p))
+               if (!ether_addr_equal(eh->h_dest, fip->ctl_src_addr) &&
+                   !ether_addr_equal(eh->h_dest, fcoe_all_vn2vn) &&
+                   !ether_addr_equal(eh->h_dest, fcoe_all_p2p))
                        goto drop;
-       } else if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
-                  compare_ether_addr(eh->h_dest, fcoe_all_enode))
+       } else if (!ether_addr_equal(eh->h_dest, fip->ctl_src_addr) &&
+                  !ether_addr_equal(eh->h_dest, fcoe_all_enode))
                goto drop;
        fiph = (struct fip_header *)skb->data;
        op = ntohs(fiph->fip_op);
@@ -1856,7 +1898,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
                 * 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)) {
+               if (ether_addr_equal(sa, (u8[6])FC_FCOE_FLOGI_MAC)) {
                        fcoe_ctlr_map_dest(fip);
                } else {
                        memcpy(fip->dest_addr, sa, ETH_ALEN);
@@ -2825,8 +2867,8 @@ unlock:
  * disabled, so that should ensure that this routine is only called
  * when nothing is happening.
  */
-void fcoe_ctlr_mode_set(struct fc_lport *lport, struct fcoe_ctlr *fip,
-                       enum fip_state fip_mode)
+static void fcoe_ctlr_mode_set(struct fc_lport *lport, struct fcoe_ctlr *fip,
+                              enum fip_state fip_mode)
 {
        void *priv;
 
index c9382d6..d0d9a2d 100644 (file)
@@ -300,29 +300,29 @@ static ssize_t store_ctlr_mode(struct device *dev,
 
        switch (ctlr->enabled) {
        case FCOE_CTLR_ENABLED:
-               LIBFCOE_SYSFS_DBG(ctlr, "Cannot change mode when enabled.");
+               LIBFCOE_SYSFS_DBG(ctlr, "Cannot change mode when enabled.\n");
                return -EBUSY;
        case FCOE_CTLR_DISABLED:
                if (!ctlr->f->set_fcoe_ctlr_mode) {
                        LIBFCOE_SYSFS_DBG(ctlr,
-                                         "Mode change not supported by LLD.");
+                                         "Mode change not supported by LLD.\n");
                        return -ENOTSUPP;
                }
 
                ctlr->mode = fcoe_parse_mode(mode);
                if (ctlr->mode == FIP_CONN_TYPE_UNKNOWN) {
-                       LIBFCOE_SYSFS_DBG(ctlr,
-                                         "Unknown mode %s provided.", buf);
+                       LIBFCOE_SYSFS_DBG(ctlr, "Unknown mode %s provided.\n",
+                                         buf);
                        return -EINVAL;
                }
 
                ctlr->f->set_fcoe_ctlr_mode(ctlr);
-               LIBFCOE_SYSFS_DBG(ctlr, "Mode changed to %s.", buf);
+               LIBFCOE_SYSFS_DBG(ctlr, "Mode changed to %s.\n", buf);
 
                return count;
        case FCOE_CTLR_UNUSED:
        default:
-               LIBFCOE_SYSFS_DBG(ctlr, "Mode change not supported.");
+               LIBFCOE_SYSFS_DBG(ctlr, "Mode change not supported.\n");
                return -ENOTSUPP;
        };
 }
@@ -653,7 +653,7 @@ static int fcoe_fcf_device_match(struct fcoe_fcf_device *new,
        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)
+           ether_addr_equal(new->mac, old->mac))
                return 1;
        return 0;
 }
index 60a1c50..1671325 100644 (file)
@@ -658,13 +658,13 @@ void fnic_update_mac_locked(struct fnic *fnic, u8 *new)
 
        if (is_zero_ether_addr(new))
                new = ctl;
-       if (!compare_ether_addr(data, new))
+       if (ether_addr_equal(data, new))
                return;
        FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
-       if (!is_zero_ether_addr(data) && compare_ether_addr(data, ctl))
+       if (!is_zero_ether_addr(data) && !ether_addr_equal(data, ctl))
                vnic_dev_del_addr(fnic->vdev, data);
        memcpy(data, new, ETH_ALEN);
-       if (compare_ether_addr(new, ctl))
+       if (!ether_addr_equal(new, ctl))
                vnic_dev_add_addr(fnic->vdev, new);
 }
 
index 5879929..1b3a094 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/export.h>
+#include <linux/log2.h>
 
 #include <scsi/fc/fc_fc2.h>
 
@@ -303,10 +304,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
                fr_eof(fp) = FC_EOF_N;
        }
 
-       /*
-        * Initialize remainig fh fields
-        * from fc_fill_fc_hdr
-        */
+       /* Initialize remaining fh fields from fc_fill_fc_hdr */
        fh->fh_ox_id = htons(ep->oxid);
        fh->fh_rx_id = htons(ep->rxid);
        fh->fh_seq_id = ep->seq.id;
@@ -362,9 +360,10 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
 
        FC_EXCH_DBG(ep, "Exchange timer armed : %d msecs\n", timer_msec);
 
-       if (queue_delayed_work(fc_exch_workqueue, &ep->timeout_work,
-                              msecs_to_jiffies(timer_msec)))
-               fc_exch_hold(ep);               /* hold for timer */
+       fc_exch_hold(ep);               /* hold for timer */
+       if (!queue_delayed_work(fc_exch_workqueue, &ep->timeout_work,
+                               msecs_to_jiffies(timer_msec)))
+               fc_exch_release(ep);
 }
 
 /**
@@ -382,6 +381,8 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
 /**
  * fc_exch_done_locked() - Complete an exchange with the exchange lock held
  * @ep: The exchange that is complete
+ *
+ * Note: May sleep if invoked from outside a response handler.
  */
 static int fc_exch_done_locked(struct fc_exch *ep)
 {
@@ -393,7 +394,6 @@ static int fc_exch_done_locked(struct fc_exch *ep)
         * ep, and in that case we only clear the resp and set it as
         * complete, so it can be reused by the timer to send the rrq.
         */
-       ep->resp = NULL;
        if (ep->state & FC_EX_DONE)
                return rc;
        ep->esb_stat |= ESB_ST_COMPLETE;
@@ -464,15 +464,21 @@ static void fc_exch_delete(struct fc_exch *ep)
 }
 
 static int fc_seq_send_locked(struct fc_lport *lport, struct fc_seq *sp,
-                      struct fc_frame *fp)
+                             struct fc_frame *fp)
 {
        struct fc_exch *ep;
        struct fc_frame_header *fh = fc_frame_header_get(fp);
-       int error;
+       int error = -ENXIO;
        u32 f_ctl;
        u8 fh_type = fh->fh_type;
 
        ep = fc_seq_exch(sp);
+
+       if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL)) {
+               fc_frame_free(fp);
+               goto out;
+       }
+
        WARN_ON(!(ep->esb_stat & ESB_ST_SEQ_INIT));
 
        f_ctl = ntoh24(fh->fh_f_ctl);
@@ -515,6 +521,9 @@ out:
  * @lport: The local port that the exchange will be sent on
  * @sp:           The sequence to be sent
  * @fp:           The frame to be sent on the exchange
+ *
+ * Note: The frame will be freed either by a direct call to fc_frame_free(fp)
+ * or indirectly by calling libfc_function_template.frame_send().
  */
 static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
                       struct fc_frame *fp)
@@ -581,6 +590,8 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
 
 /*
  * Set the response handler for the exchange associated with a sequence.
+ *
+ * Note: May sleep if invoked from outside a response handler.
  */
 static void fc_seq_set_resp(struct fc_seq *sp,
                            void (*resp)(struct fc_seq *, struct fc_frame *,
@@ -588,8 +599,18 @@ static void fc_seq_set_resp(struct fc_seq *sp,
                            void *arg)
 {
        struct fc_exch *ep = fc_seq_exch(sp);
+       DEFINE_WAIT(wait);
 
        spin_lock_bh(&ep->ex_lock);
+       while (ep->resp_active && ep->resp_task != current) {
+               prepare_to_wait(&ep->resp_wq, &wait, TASK_UNINTERRUPTIBLE);
+               spin_unlock_bh(&ep->ex_lock);
+
+               schedule();
+
+               spin_lock_bh(&ep->ex_lock);
+       }
+       finish_wait(&ep->resp_wq, &wait);
        ep->resp = resp;
        ep->arg = arg;
        spin_unlock_bh(&ep->ex_lock);
@@ -622,27 +643,31 @@ static int fc_exch_abort_locked(struct fc_exch *ep,
        if (!sp)
                return -ENOMEM;
 
-       ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL;
        if (timer_msec)
                fc_exch_timer_set_locked(ep, timer_msec);
 
-       /*
-        * If not logged into the fabric, don't send ABTS but leave
-        * sequence active until next timeout.
-        */
-       if (!ep->sid)
-               return 0;
-
-       /*
-        * Send an abort for the sequence that timed out.
-        */
-       fp = fc_frame_alloc(ep->lp, 0);
-       if (fp) {
-               fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid,
-                              FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
-               error = fc_seq_send_locked(ep->lp, sp, fp);
-       } else
-               error = -ENOBUFS;
+       if (ep->sid) {
+               /*
+                * Send an abort for the sequence that timed out.
+                */
+               fp = fc_frame_alloc(ep->lp, 0);
+               if (fp) {
+                       ep->esb_stat |= ESB_ST_SEQ_INIT;
+                       fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid,
+                                      FC_TYPE_BLS, FC_FC_END_SEQ |
+                                      FC_FC_SEQ_INIT, 0);
+                       error = fc_seq_send_locked(ep->lp, sp, fp);
+               } else {
+                       error = -ENOBUFS;
+               }
+       } else {
+               /*
+                * If not logged into the fabric, don't send ABTS but leave
+                * sequence active until next timeout.
+                */
+               error = 0;
+       }
+       ep->esb_stat |= ESB_ST_ABNORMAL;
        return error;
 }
 
@@ -669,6 +694,61 @@ static int fc_seq_exch_abort(const struct fc_seq *req_sp,
 }
 
 /**
+ * fc_invoke_resp() - invoke ep->resp()
+ *
+ * Notes:
+ * It is assumed that after initialization finished (this means the
+ * first unlock of ex_lock after fc_exch_alloc()) ep->resp and ep->arg are
+ * modified only via fc_seq_set_resp(). This guarantees that none of these
+ * two variables changes if ep->resp_active > 0.
+ *
+ * If an fc_seq_set_resp() call is busy modifying ep->resp and ep->arg when
+ * this function is invoked, the first spin_lock_bh() call in this function
+ * will wait until fc_seq_set_resp() has finished modifying these variables.
+ *
+ * Since fc_exch_done() invokes fc_seq_set_resp() it is guaranteed that that
+ * ep->resp() won't be invoked after fc_exch_done() has returned.
+ *
+ * The response handler itself may invoke fc_exch_done(), which will clear the
+ * ep->resp pointer.
+ *
+ * Return value:
+ * Returns true if and only if ep->resp has been invoked.
+ */
+static bool fc_invoke_resp(struct fc_exch *ep, struct fc_seq *sp,
+                          struct fc_frame *fp)
+{
+       void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+       void *arg;
+       bool res = false;
+
+       spin_lock_bh(&ep->ex_lock);
+       ep->resp_active++;
+       if (ep->resp_task != current)
+               ep->resp_task = !ep->resp_task ? current : NULL;
+       resp = ep->resp;
+       arg = ep->arg;
+       spin_unlock_bh(&ep->ex_lock);
+
+       if (resp) {
+               resp(sp, fp, arg);
+               res = true;
+       } else if (!IS_ERR(fp)) {
+               fc_frame_free(fp);
+       }
+
+       spin_lock_bh(&ep->ex_lock);
+       if (--ep->resp_active == 0)
+               ep->resp_task = NULL;
+       spin_unlock_bh(&ep->ex_lock);
+
+       if (ep->resp_active == 0)
+               wake_up(&ep->resp_wq);
+
+       return res;
+}
+
+/**
  * fc_exch_timeout() - Handle exchange timer expiration
  * @work: The work_struct identifying the exchange that timed out
  */
@@ -677,8 +757,6 @@ static void fc_exch_timeout(struct work_struct *work)
        struct fc_exch *ep = container_of(work, struct fc_exch,
                                          timeout_work.work);
        struct fc_seq *sp = &ep->seq;
-       void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
-       void *arg;
        u32 e_stat;
        int rc = 1;
 
@@ -696,16 +774,13 @@ static void fc_exch_timeout(struct work_struct *work)
                        fc_exch_rrq(ep);
                goto done;
        } else {
-               resp = ep->resp;
-               arg = ep->arg;
-               ep->resp = NULL;
                if (e_stat & ESB_ST_ABNORMAL)
                        rc = fc_exch_done_locked(ep);
                spin_unlock_bh(&ep->ex_lock);
                if (!rc)
                        fc_exch_delete(ep);
-               if (resp)
-                       resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
+               fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_TIMEOUT));
+               fc_seq_set_resp(sp, NULL, ep->arg);
                fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
                goto done;
        }
@@ -792,6 +867,8 @@ hit:
        ep->f_ctl = FC_FC_FIRST_SEQ;    /* next seq is first seq */
        ep->rxid = FC_XID_UNKNOWN;
        ep->class = mp->class;
+       ep->resp_active = 0;
+       init_waitqueue_head(&ep->resp_wq);
        INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout);
 out:
        return ep;
@@ -838,8 +915,10 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
                pool = per_cpu_ptr(mp->pool, xid & fc_cpu_mask);
                spin_lock_bh(&pool->lock);
                ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order);
-               if (ep && ep->xid == xid)
+               if (ep) {
+                       WARN_ON(ep->xid != xid);
                        fc_exch_hold(ep);
+               }
                spin_unlock_bh(&pool->lock);
        }
        return ep;
@@ -850,6 +929,8 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
  * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and
  *                 the memory allocated for the related objects may be freed.
  * @sp: The sequence that has completed
+ *
+ * Note: May sleep if invoked from outside a response handler.
  */
 static void fc_exch_done(struct fc_seq *sp)
 {
@@ -859,6 +940,8 @@ static void fc_exch_done(struct fc_seq *sp)
        spin_lock_bh(&ep->ex_lock);
        rc = fc_exch_done_locked(ep);
        spin_unlock_bh(&ep->ex_lock);
+
+       fc_seq_set_resp(sp, NULL, ep->arg);
        if (!rc)
                fc_exch_delete(ep);
 }
@@ -987,6 +1070,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
                }
        }
 
+       spin_lock_bh(&ep->ex_lock);
        /*
         * At this point, we have the exchange held.
         * Find or create the sequence.
@@ -1014,11 +1098,11 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
                                 * sending RSP, hence write request on other
                                 * end never finishes.
                                 */
-                               spin_lock_bh(&ep->ex_lock);
                                sp->ssb_stat |= SSB_ST_RESP;
                                sp->id = fh->fh_seq_id;
-                               spin_unlock_bh(&ep->ex_lock);
                        } else {
+                               spin_unlock_bh(&ep->ex_lock);
+
                                /* sequence/exch should exist */
                                reject = FC_RJT_SEQ_ID;
                                goto rel;
@@ -1029,6 +1113,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
 
        if (f_ctl & FC_FC_SEQ_INIT)
                ep->esb_stat |= ESB_ST_SEQ_INIT;
+       spin_unlock_bh(&ep->ex_lock);
 
        fr_seq(fp) = sp;
 out:
@@ -1291,21 +1376,23 @@ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
 
        if (!ep)
                goto reject;
+
+       fp = fc_frame_alloc(ep->lp, sizeof(*ap));
+       if (!fp)
+               goto free;
+
        spin_lock_bh(&ep->ex_lock);
        if (ep->esb_stat & ESB_ST_COMPLETE) {
                spin_unlock_bh(&ep->ex_lock);
+
+               fc_frame_free(fp);
                goto reject;
        }
-       if (!(ep->esb_stat & ESB_ST_REC_QUAL))
+       if (!(ep->esb_stat & ESB_ST_REC_QUAL)) {
+               ep->esb_stat |= ESB_ST_REC_QUAL;
                fc_exch_hold(ep);               /* hold for REC_QUAL */
-       ep->esb_stat |= ESB_ST_ABNORMAL | ESB_ST_REC_QUAL;
-       fc_exch_timer_set_locked(ep, ep->r_a_tov);
-
-       fp = fc_frame_alloc(ep->lp, sizeof(*ap));
-       if (!fp) {
-               spin_unlock_bh(&ep->ex_lock);
-               goto free;
        }
+       fc_exch_timer_set_locked(ep, ep->r_a_tov);
        fh = fc_frame_header_get(fp);
        ap = fc_frame_payload_get(fp, sizeof(*ap));
        memset(ap, 0, sizeof(*ap));
@@ -1319,14 +1406,16 @@ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
        }
        sp = fc_seq_start_next_locked(sp);
        fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS);
+       ep->esb_stat |= ESB_ST_ABNORMAL;
        spin_unlock_bh(&ep->ex_lock);
+
+free:
        fc_frame_free(rx_fp);
        return;
 
 reject:
        fc_exch_send_ba_rjt(rx_fp, FC_BA_RJT_UNABLE, FC_BA_RJT_INV_XID);
-free:
-       fc_frame_free(rx_fp);
+       goto free;
 }
 
 /**
@@ -1416,9 +1505,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
                 * If new exch resp handler is valid then call that
                 * first.
                 */
-               if (ep->resp)
-                       ep->resp(sp, fp, ep->arg);
-               else
+               if (!fc_invoke_resp(ep, sp, fp))
                        lport->tt.lport_recv(lport, fp);
                fc_exch_release(ep);    /* release from lookup */
        } else {
@@ -1442,8 +1529,6 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
        struct fc_exch *ep;
        enum fc_sof sof;
        u32 f_ctl;
-       void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
-       void *ex_resp_arg;
        int rc;
 
        ep = fc_exch_find(mp, ntohs(fh->fh_ox_id));
@@ -1478,19 +1563,19 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
 
        f_ctl = ntoh24(fh->fh_f_ctl);
        fr_seq(fp) = sp;
+
+       spin_lock_bh(&ep->ex_lock);
        if (f_ctl & FC_FC_SEQ_INIT)
                ep->esb_stat |= ESB_ST_SEQ_INIT;
+       spin_unlock_bh(&ep->ex_lock);
 
        if (fc_sof_needs_ack(sof))
                fc_seq_send_ack(sp, fp);
-       resp = ep->resp;
-       ex_resp_arg = ep->arg;
 
        if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T &&
            (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
            (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
                spin_lock_bh(&ep->ex_lock);
-               resp = ep->resp;
                rc = fc_exch_done_locked(ep);
                WARN_ON(fc_seq_exch(sp) != ep);
                spin_unlock_bh(&ep->ex_lock);
@@ -1511,10 +1596,8 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
         * If new exch resp handler is valid then call that
         * first.
         */
-       if (resp)
-               resp(sp, fp, ex_resp_arg);
-       else
-               fc_frame_free(fp);
+       fc_invoke_resp(ep, sp, fp);
+
        fc_exch_release(ep);
        return;
 rel:
@@ -1553,8 +1636,6 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
  */
 static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
 {
-       void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
-       void *ex_resp_arg;
        struct fc_frame_header *fh;
        struct fc_ba_acc *ap;
        struct fc_seq *sp;
@@ -1599,9 +1680,6 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
                break;
        }
 
-       resp = ep->resp;
-       ex_resp_arg = ep->arg;
-
        /* do we need to do some other checks here. Can we reuse more of
         * fc_exch_recv_seq_resp
         */
@@ -1613,17 +1691,14 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
            ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ)
                rc = fc_exch_done_locked(ep);
        spin_unlock_bh(&ep->ex_lock);
+
+       fc_exch_hold(ep);
        if (!rc)
                fc_exch_delete(ep);
-
-       if (resp)
-               resp(sp, fp, ex_resp_arg);
-       else
-               fc_frame_free(fp);
-
+       fc_invoke_resp(ep, sp, fp);
        if (has_rec)
                fc_exch_timer_set(ep, ep->r_a_tov);
-
+       fc_exch_release(ep);
 }
 
 /**
@@ -1662,7 +1737,7 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
                        break;
                default:
                        if (ep)
-                               FC_EXCH_DBG(ep, "BLS rctl %x - %s received",
+                               FC_EXCH_DBG(ep, "BLS rctl %x - %s received\n",
                                            fh->fh_r_ctl,
                                            fc_exch_rctl_name(fh->fh_r_ctl));
                        break;
@@ -1745,32 +1820,33 @@ static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,
 /**
  * fc_exch_reset() - Reset an exchange
  * @ep: The exchange to be reset
+ *
+ * Note: May sleep if invoked from outside a response handler.
  */
 static void fc_exch_reset(struct fc_exch *ep)
 {
        struct fc_seq *sp;
-       void (*resp)(struct fc_seq *, struct fc_frame *, void *);
-       void *arg;
        int rc = 1;
 
        spin_lock_bh(&ep->ex_lock);
        fc_exch_abort_locked(ep, 0);
        ep->state |= FC_EX_RST_CLEANUP;
        fc_exch_timer_cancel(ep);
-       resp = ep->resp;
-       ep->resp = NULL;
        if (ep->esb_stat & ESB_ST_REC_QUAL)
                atomic_dec(&ep->ex_refcnt);     /* drop hold for rec_qual */
        ep->esb_stat &= ~ESB_ST_REC_QUAL;
-       arg = ep->arg;
        sp = &ep->seq;
        rc = fc_exch_done_locked(ep);
        spin_unlock_bh(&ep->ex_lock);
+
+       fc_exch_hold(ep);
+
        if (!rc)
                fc_exch_delete(ep);
 
-       if (resp)
-               resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
+       fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_CLOSED));
+       fc_seq_set_resp(sp, NULL, ep->arg);
+       fc_exch_release(ep);
 }
 
 /**
@@ -1956,13 +2032,13 @@ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
 
        switch (op) {
        case ELS_LS_RJT:
-               FC_EXCH_DBG(aborted_ep, "LS_RJT for RRQ");
+               FC_EXCH_DBG(aborted_ep, "LS_RJT for RRQ\n");
                /* fall through */
        case ELS_LS_ACC:
                goto cleanup;
        default:
-               FC_EXCH_DBG(aborted_ep, "unexpected response op %x "
-                           "for RRQ", op);
+               FC_EXCH_DBG(aborted_ep, "unexpected response op %x for RRQ\n",
+                           op);
                return;
        }
 
@@ -2533,13 +2609,8 @@ int fc_setup_exch_mgr(void)
         * cpu on which exchange originated by simple bitwise
         * AND operation between fc_cpu_mask and exchange id.
         */
-       fc_cpu_mask = 1;
-       fc_cpu_order = 0;
-       while (fc_cpu_mask < nr_cpu_ids) {
-               fc_cpu_mask <<= 1;
-               fc_cpu_order++;
-       }
-       fc_cpu_mask--;
+       fc_cpu_order = ilog2(roundup_pow_of_two(nr_cpu_ids));
+       fc_cpu_mask = (1 << fc_cpu_order) - 1;
 
        fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");
        if (!fc_exch_workqueue)
index 5fd0f1f..1d7e76e 100644 (file)
@@ -902,7 +902,8 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
        /*
         * Check for missing or extra data frames.
         */
-       if (unlikely(fsp->xfer_len != expected_len)) {
+       if (unlikely(fsp->cdb_status == SAM_STAT_GOOD &&
+                    fsp->xfer_len != expected_len)) {
                if (fsp->xfer_len < expected_len) {
                        /*
                         * Some data may be queued locally,
@@ -955,12 +956,11 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
                 * Test for transport underrun, independent of response
                 * underrun status.
                 */
-               if (fsp->xfer_len < fsp->data_len && !fsp->io_status &&
+               if (fsp->cdb_status == SAM_STAT_GOOD &&
+                   fsp->xfer_len < fsp->data_len && !fsp->io_status &&
                    (!(fsp->scsi_comp_flags & FCP_RESID_UNDER) ||
-                    fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) {
+                    fsp->xfer_len < fsp->data_len - fsp->scsi_resid))
                        fsp->status_code = FC_DATA_UNDRUN;
-                       fsp->io_status = 0;
-               }
        }
 
        seq = fsp->seq_ptr;
index f04d15c..e01a298 100644 (file)
@@ -516,7 +516,7 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport,
  * @lport: The local port receiving the LOGO
  * @fp:           The LOGO request frame
  *
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
 static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
@@ -1088,7 +1088,7 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
 {
        unsigned long delay = 0;
        FC_LPORT_DBG(lport, "Error %ld in state %s, retries %d\n",
-                    PTR_ERR(fp), fc_lport_state(lport),
+                    IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_lport_state(lport),
                     lport->retry_count);
 
        if (PTR_ERR(fp) == -FC_EX_CLOSED)
index c710d90..589ff9a 100644 (file)
@@ -1705,7 +1705,7 @@ reject:
  * @rdata: The remote port that sent the PRLI request
  * @rx_fp: The PRLI request frame
  *
- * Locking Note: The rport lock is exected to be held before calling
+ * Locking Note: The rport lock is expected to be held before calling
  * this function.
  */
 static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
@@ -1824,7 +1824,7 @@ drop:
  * @rdata: The remote port that sent the PRLO request
  * @rx_fp: The PRLO request frame
  *
- * Locking Note: The rport lock is exected to be held before calling
+ * Locking Note: The rport lock is expected to be held before calling
  * this function.
  */
 static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
@@ -1895,7 +1895,7 @@ drop:
  * @lport: The local port that received the LOGO request
  * @fp:           The LOGO request frame
  *
- * Locking Note: The rport lock is exected to be held before calling
+ * Locking Note: The rport lock is expected to be held before calling
  * this function.
  */
 static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
index f87777d..0b26714 100644 (file)
@@ -104,7 +104,7 @@ struct fc_esb {
  * esb_e_stat - flags from FC-FS-2 T11/1619-D Rev 0.90.
  */
 #define ESB_ST_RESP         (1 << 31)   /* responder to exchange */
-#define ESB_ST_SEQ_INIT     (1 << 30)   /* port holds sequence initiaive */
+#define ESB_ST_SEQ_INIT     (1 << 30)   /* port holds sequence initiative */
 #define ESB_ST_COMPLETE     (1 << 29)   /* exchange is complete */
 #define ESB_ST_ABNORMAL     (1 << 28)   /* abnormal ending condition */
 #define ESB_ST_REC_QUAL     (1 << 26)   /* recovery qualifier active */
index e1379b4..52beadf 100644 (file)
@@ -410,6 +410,12 @@ struct fc_seq {
  * @fh_type:      The frame type
  * @class:        The class of service
  * @seq:          The sequence in use on this exchange
+ * @resp_active:  Number of tasks that are concurrently executing @resp().
+ * @resp_task:    If @resp_active > 0, either the task executing @resp(), the
+ *                task that has been interrupted to execute the soft-IRQ
+ *                executing @resp() or NULL if more than one task is executing
+ *                @resp concurrently.
+ * @resp_wq:      Waitqueue for the tasks waiting on @resp_active.
  * @resp:         Callback for responses on this exchange
  * @destructor:   Called when destroying the exchange
  * @arg:          Passed as a void pointer to the resp() callback
@@ -441,6 +447,9 @@ struct fc_exch {
        u32                 r_a_tov;
        u32                 f_ctl;
        struct fc_seq       seq;
+       int                 resp_active;
+       struct task_struct  *resp_task;
+       wait_queue_head_t   resp_wq;
        void                (*resp)(struct fc_seq *, struct fc_frame *, void *);
        void                *arg;
        void                (*destructor)(struct fc_seq *, void *);
index 4427393..de7e3ee 100644 (file)
@@ -90,6 +90,7 @@ enum fip_state {
  * @lp:                   &fc_lport: libfc local port.
  * @sel_fcf:      currently selected FCF, or NULL.
  * @fcfs:         list of discovered FCFs.
+ * @cdev:          (Optional) pointer to sysfs fcoe_ctlr_device.
  * @fcf_count:    number of discovered FCF entries.
  * @sol_time:     time when a multicast solicitation was last sent.
  * @sel_time:     time after which to select an FCF.
@@ -127,6 +128,7 @@ struct fcoe_ctlr {
        struct fc_lport *lp;
        struct fcoe_fcf *sel_fcf;
        struct list_head fcfs;
+       struct fcoe_ctlr_device *cdev;
        u16 fcf_count;
        unsigned long sol_time;
        unsigned long sel_time;
@@ -168,8 +170,11 @@ static inline void *fcoe_ctlr_priv(const struct fcoe_ctlr *ctlr)
        return (void *)(ctlr + 1);
 }
 
+/*
+ * This assumes that the fcoe_ctlr (x) is allocated with the fcoe_ctlr_device.
+ */
 #define fcoe_ctlr_to_ctlr_dev(x)                                       \
-       (struct fcoe_ctlr_device *)(((struct fcoe_ctlr_device *)(x)) - 1)
+       (x)->cdev
 
 /**
  * struct fcoe_fcf - Fibre-Channel Forwarder