/** List of upper-layer protocols */
struct list_head ulps;
- /** Active usage count */
+ /** Active usage count
+ *
+ * A peer (and attached ULPs) may be created in response to
+ * unsolicited login requests received via the fabric. We
+ * track our own active usage count independently of the
+ * existence of the peer, so that if the peer becomes logged
+ * out (e.g. due to a link failure) then we know whether or
+ * not we should attempt to relogin.
+ */
unsigned int usage;
};
/** Service parameter length */
size_t param_len;
- /** Active usage count */
- unsigned int usage;
+ /** Active users of this upper-layer protocol
+ *
+ * As with peers, an upper-layer protocol may be created in
+ * response to an unsolicited login request received via the
+ * fabric. This list records the number of active users of
+ * the ULP; the number of entries in the list is equivalent to
+ * the peer usage count.
+ */
+ struct list_head users;
};
/** Fibre Channel upper-layer protocol flags */
FC_ULP_ORIGINATED_LOGIN_OK = 0x0001,
};
+/** A Fibre Channel upper-layer protocol user */
+struct fc_ulp_user {
+ /** Fibre Channel upper layer protocol */
+ struct fc_ulp *ulp;
+ /** List of users */
+ struct list_head list;
+};
+
/**
* Get reference to Fibre Channel upper-layer protocol
*
fc_ulp_get_port_id_type ( struct fc_port *port,
const struct fc_port_id *peer_port_id,
unsigned int type );
-extern void fc_ulp_increment ( struct fc_ulp *ulp );
-extern void fc_ulp_decrement ( struct fc_ulp *ulp );
+extern void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user );
+extern void fc_ulp_detach ( struct fc_ulp_user *user );
extern int fc_ulp_login ( struct fc_ulp *ulp, const void *param,
size_t param_len, int originated );
extern void fc_ulp_logout ( struct fc_ulp *ulp, int rc );
fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
/* Sanity check */
- assert ( ulp->usage == 0 );
+ assert ( list_empty ( &ulp->users ) );
/* Stop link monitor */
fc_link_stop ( &ulp->link );
}
/**
- * Increment Fibre Channel upper-layer protocol active usage count
+ * Attach Fibre Channel upper-layer protocol user
*
- * @v ulp Fibre Channel ulp
+ * @v ulp Fibre Channel upper-layer protocol
+ * @v user Fibre Channel upper-layer protocol user
*/
-void fc_ulp_increment ( struct fc_ulp *ulp ) {
+void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ) {
+
+ /* Sanity check */
+ assert ( user->ulp == NULL );
/* Increment peer's usage count */
fc_peer_increment ( ulp->peer );
- /* Increment our usage count */
- ulp->usage++;
+ /* Attach user */
+ user->ulp = fc_ulp_get ( ulp );
+ list_add ( &user->list, &ulp->users );
}
/**
- * Decrement Fibre Channel upper-layer protocol active usage count
+ * Detach Fibre Channel upper-layer protocol user
*
- * @v ulp Fibre Channel ulp
+ * @v user Fibre Channel upper-layer protocol user
*/
-void fc_ulp_decrement ( struct fc_ulp *ulp ) {
+void fc_ulp_detach ( struct fc_ulp_user *user ) {
+ struct fc_ulp *ulp = user->ulp;
- /* Sanity check */
- assert ( ulp->usage > 0 );
+ /* Do nothing if not attached */
+ if ( ! ulp )
+ return;
- /* Decrement our usage count and log out if we reach zero */
- if ( --(ulp->usage) == 0 )
+ /* Sanity checks */
+ list_check_contains ( user, &ulp->users, list );
+
+ /* Detach user and log out if no users remain */
+ list_del ( &user->list );
+ if ( list_empty ( &ulp->users ) )
fc_ulp_logout ( ulp, 0 );
/* Decrement our peer's usage count */
fc_peer_decrement ( ulp->peer );
+
+ /* Drop reference */
+ user->ulp = NULL;
+ fc_ulp_put ( ulp );
}
/**
fc_link_err ( &ulp->link, rc );
/* Close ULP if there are no clients attached */
- if ( ulp->usage == 0 )
+ if ( list_empty ( &ulp->users ) )
fc_ulp_close ( ulp, rc );
}
ulp->peer = fc_peer_get ( peer );
list_add_tail ( &ulp->list, &peer->ulps );
ulp->type = type;
+ INIT_LIST_HEAD ( &ulp->users );
/* Start link state monitor */
fc_link_start ( &ulp->link );
struct fcp_device {
/** Reference count */
struct refcnt refcnt;
- /** Fibre Channel upper-layer protocol */
- struct fc_ulp *ulp;
+ /** Fibre Channel upper-layer protocol user */
+ struct fc_ulp_user user;
/** SCSI command issuing interface */
struct interface scsi;
/** List of active commands */
static int fcpdev_scsi_command ( struct fcp_device *fcpdev,
struct interface *parent,
struct scsi_cmd *command ) {
- struct fcp_prli_service_parameters *param = fcpdev->ulp->param;
+ struct fcp_prli_service_parameters *param = fcpdev->user.ulp->param;
struct fcp_command *fcpcmd;
int xchg_id;
int rc;
/* Check link */
- if ( ( rc = fcpdev->ulp->link.rc ) != 0 ) {
+ if ( ( rc = fcpdev->user.ulp->link.rc ) != 0 ) {
DBGC ( fcpdev, "FCP %p could not issue command while link is "
"down: %s\n", fcpdev, strerror ( rc ) );
goto err_link;
/* Check target capability */
assert ( param != NULL );
- assert ( fcpdev->ulp->param_len >= sizeof ( *param ) );
+ assert ( fcpdev->user.ulp->param_len >= sizeof ( *param ) );
if ( ! ( param->flags & htonl ( FCP_PRLI_TARGET ) ) ) {
DBGC ( fcpdev, "FCP %p could not issue command: not a target\n",
fcpdev );
/* Create new exchange */
if ( ( xchg_id = fc_xchg_originate ( &fcpcmd->xchg,
- fcpdev->ulp->peer->port,
- &fcpdev->ulp->peer->port_id,
+ fcpdev->user.ulp->peer->port,
+ &fcpdev->user.ulp->peer->port_id,
FC_TYPE_FCP ) ) < 0 ) {
rc = xchg_id;
DBGC ( fcpdev, "FCP %p could not create exchange: %s\n",
}
/* Drop reference to ULP */
- if ( fcpdev->ulp ) {
- fc_ulp_decrement ( fcpdev->ulp );
- fc_ulp_put ( fcpdev->ulp );
- fcpdev->ulp = NULL;
- }
+ fc_ulp_detach ( &fcpdev->user );
}
/**
* @ret len Length of window
*/
static size_t fcpdev_window ( struct fcp_device *fcpdev ) {
- return ( fc_link_ok ( &fcpdev->ulp->link ) ? ~( ( size_t ) 0 ) : 0 );
+ return ( fc_link_ok ( &fcpdev->user.ulp->link ) ?
+ ~( ( size_t ) 0 ) : 0 );
}
/**
/* We know the underlying device only if the link is up;
* otherwise we don't have a port to examine.
*/
- if ( ! fc_link_ok ( &fcpdev->ulp->link ) ) {
+ if ( ! fc_link_ok ( &fcpdev->user.ulp->link ) ) {
DBGC ( fcpdev, "FCP %p doesn't know underlying device "
"until link is up\n", fcpdev );
return NULL;
}
/* Hand off to port's transport interface */
- assert ( fcpdev->ulp->peer->port != NULL );
- return identify_device ( &fcpdev->ulp->peer->port->transport );
+ assert ( fcpdev->user.ulp->peer->port != NULL );
+ return identify_device ( &fcpdev->user.ulp->peer->port->transport );
}
/** FCP device SCSI interface operations */
ref_init ( &fcpdev->refcnt, NULL );
intf_init ( &fcpdev->scsi, &fcpdev_scsi_desc, &fcpdev->refcnt );
INIT_LIST_HEAD ( &fcpdev->fcpcmds );
- fcpdev->ulp = fc_ulp_get ( ulp );
- fc_ulp_increment ( fcpdev->ulp );
+ fc_ulp_attach ( ulp, &fcpdev->user );
DBGC ( fcpdev, "FCP %p opened for %s\n", fcpdev, fc_ntoa ( wwn ) );
}
list_for_each_entry ( ulp, &peer->ulps, list ) {
- printf ( " [Type %02x usage %d link:",
- ulp->type, ulp->usage );
+ printf ( " [Type %02x link:", ulp->type );
if ( fc_link_ok ( &ulp->link ) ) {
printf ( " up, params" );
param = ulp->param;