]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[fc] Send xfer_window_changed() when FCP link is established
authorMichael Brown <mcb30@ipxe.org>
Fri, 24 Jun 2011 22:16:17 +0000 (23:16 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 28 Jun 2011 13:45:07 +0000 (14:45 +0100)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/fc.h
src/net/fc.c
src/net/fcp.c

index 06d38baf9000994585d6164b34cf62e81fe54f11..6689f394e489decc67c43767daf3e1461698deb5 100644 (file)
@@ -455,6 +455,13 @@ struct fc_ulp_user {
        struct fc_ulp *ulp;
        /** List of users */
        struct list_head list;
+       /** Containing object reference count, or NULL */
+       struct refcnt *refcnt;
+       /** Examine link state
+        *
+        * @v user              Fibre Channel upper-layer-protocol user
+        */
+       void ( * examine ) ( struct fc_ulp_user *user );
 };
 
 /**
@@ -479,6 +486,43 @@ fc_ulp_put ( struct fc_ulp *ulp ) {
        ref_put ( &ulp->refcnt );
 }
 
+/**
+ * Get reference to Fibre Channel upper-layer protocol user
+ *
+ * @v user             Fibre Channel upper-layer protocol user
+ * @ret user           Fibre Channel upper-layer protocol user
+ */
+static inline __attribute__ (( always_inline )) struct fc_ulp_user *
+fc_ulp_user_get ( struct fc_ulp_user *user ) {
+       ref_get ( user->refcnt );
+       return user;
+}
+
+/**
+ * Drop reference to Fibre Channel upper-layer protocol user
+ *
+ * @v user             Fibre Channel upper-layer protocol user
+ */
+static inline __attribute__ (( always_inline )) void
+fc_ulp_user_put ( struct fc_ulp_user *user ) {
+       ref_put ( user->refcnt );
+}
+
+/**
+ * Initialise Fibre Channel upper-layer protocol user
+ *
+ * @v user             Fibre Channel upper-layer protocol user
+ * @v examine          Examine link state method
+ * @v refcnt           Containing object reference count, or NULL
+ */
+static inline __attribute__ (( always_inline )) void
+fc_ulp_user_init ( struct fc_ulp_user *user,
+                  void ( * examine ) ( struct fc_ulp_user *user ),
+                  struct refcnt *refcnt ) {
+       user->examine = examine;
+       user->refcnt = refcnt;
+}
+
 extern struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn,
                                             unsigned int type );
 extern struct fc_ulp *
index a94456c82b0fecb567e46ce819be8bfc67ef056b..977ad07c7d5a9b8ef57e2baff65caab265d6d214 100644 (file)
@@ -1651,6 +1651,8 @@ void fc_ulp_detach ( struct fc_ulp_user *user ) {
  */
 int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
                   int originated ) {
+       struct fc_ulp_user *user;
+       struct fc_ulp_user *tmp;
 
        /* Perform implicit logout if logged in and service parameters differ */
        if ( fc_link_ok ( &ulp->link ) &&
@@ -1659,6 +1661,22 @@ int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
                fc_ulp_logout ( ulp, 0 );
        }
 
+       /* Work around a bug in some versions of the Linux Fibre
+        * Channel stack, which fail to fully initialise image pairs
+        * established via a PRLI originated by the Linux stack
+        * itself.
+        */
+       if ( originated )
+               ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK;
+       if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) {
+               DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around "
+                      "Linux bug\n",
+                      fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
+               fc_link_stop ( &ulp->link );
+               fc_link_start ( &ulp->link );
+               return 0;
+       }
+
        /* Log in, if applicable */
        if ( ! fc_link_ok ( &ulp->link ) ) {
 
@@ -1685,18 +1703,11 @@ int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
        /* Record login */
        fc_link_up ( &ulp->link );
 
-       /* Work around a bug in some versions of the Linux Fibre
-        * Channel stack, which fail to fully initialise image pairs
-        * established via a PRLI originated by the Linux stack
-        * itself.
-        */
-       if ( originated )
-               ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK;
-       if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) {
-               DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around "
-                      "Linux bug\n",
-                      fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
-               fc_link_start ( &ulp->link );
+       /* Notify users of link state change */
+       list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
+               fc_ulp_user_get ( user );
+               user->examine ( user );
+               fc_ulp_user_put ( user );
        }
 
        return 0;
@@ -1709,6 +1720,8 @@ int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
  * @v rc               Reason for logout
  */
 void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) {
+       struct fc_ulp_user *user;
+       struct fc_ulp_user *tmp;
 
        DBGC ( ulp, "FCULP %s/%02x logged out: %s\n",
               fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
@@ -1726,6 +1739,13 @@ void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) {
        /* Record logout */
        fc_link_err ( &ulp->link, rc );
 
+       /* Notify users of link state change */
+       list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
+               fc_ulp_user_get ( user );
+               user->examine ( user );
+               fc_ulp_user_put ( user );
+       }
+
        /* Close ULP if there are no clients attached */
        if ( list_empty ( &ulp->users ) )
                fc_ulp_close ( ulp, rc );
index 28d2095d7680c4350eb9d51efcb4cfb43b1e0815..419fea3ec2ea7a4b66621c57ccce2867333bae3d 100644 (file)
@@ -920,6 +920,26 @@ static struct interface_operation fcpdev_scsi_op[] = {
 static struct interface_descriptor fcpdev_scsi_desc =
        INTF_DESC ( struct fcp_device, scsi, fcpdev_scsi_op );
 
+/**
+ * Examine FCP ULP link state
+ *
+ * @v user             Fibre Channel upper-layer protocol user
+ */
+static void fcpdev_examine ( struct fc_ulp_user *user ) {
+       struct fcp_device *fcpdev =
+               container_of ( user, struct fcp_device, user );
+
+       if ( fc_link_ok ( &fcpdev->user.ulp->link ) ) {
+               DBGC ( fcpdev, "FCP %p link is up\n", fcpdev );
+       } else {
+               DBGC ( fcpdev, "FCP %p link is down: %s\n",
+                      fcpdev, strerror ( fcpdev->user.ulp->link.rc ) );
+       }
+
+       /* Notify SCSI layer of window change */
+       xfer_window_changed ( &fcpdev->scsi );
+}
+
 /**
  * Open FCP device
  *
@@ -950,10 +970,13 @@ static int fcpdev_open ( struct interface *parent, struct fc_name *wwn,
        ref_init ( &fcpdev->refcnt, NULL );
        intf_init ( &fcpdev->scsi, &fcpdev_scsi_desc, &fcpdev->refcnt );
        INIT_LIST_HEAD ( &fcpdev->fcpcmds );
-       fc_ulp_attach ( ulp, &fcpdev->user );
+       fc_ulp_user_init ( &fcpdev->user, fcpdev_examine, &fcpdev->refcnt );
 
        DBGC ( fcpdev, "FCP %p opened for %s\n", fcpdev, fc_ntoa ( wwn ) );
 
+       /* Attach to Fibre Channel ULP */
+       fc_ulp_attach ( ulp, &fcpdev->user );
+
        /* Preserve parameters required for boot firmware table */
        memcpy ( &fcpdev->wwn, wwn, sizeof ( fcpdev->wwn ) );
        memcpy ( &fcpdev->lun, lun, sizeof ( fcpdev->lun ) );