Just to complete this thread, here is the version we ended up with. Changes since last time are that checks for errors on the ioctl for setting non-blocking mode; removes the local variable "lbolt" entirely; avoids the final call to ksocket_connect if the callback occurs; correctly initializes the memory for the structure used for communication with the callback; and shuts down the socket on connect timeout to leave the socket in a known state. Thanks to all for your help with this.

/* used by idm_so_timed_socket_connect */
typedef struct idm_so_timed_socket_s {
        kcondvar_t      it_cv;
        boolean_t       it_callback_called;
        int             it_socket_error_code;
} idm_so_timed_socket_t;

/* Called by kernel sockets when the connection has been accepted or
* rejected.
* In early volo, a "disconnect" was sent instead of"connectfailed",
* so we check for both.
/* ARGSUSED */
void *
idm_so_timed_socket_connect_cb(ksocket_t ks,
   ksocket_callback_event_t ev, void *arg, uintptr_t info)
{
        idm_so_timed_socket_t   *itp = (idm_so_timed_socket_t *)arg;
        ASSERT(itp != NULL);
        ASSERT(ev == KSOCKET_EV_CONNECTED ||
            ev == KSOCKET_EV_CONNECTFAILED ||
            ev == KSOCKET_EV_DISCONNECTED);

        mutex_enter(&idm_so_timed_socket_mutex);
        itp->it_callback_called = B_TRUE;
if (ev == KSOCKET_EV_CONNECTED) itp->it_callback_error_code = 0;
        else {
                /* Make sure the error code is non-zero on error */
                if (info == 0)
                        info = ECONNRESET;
                itp->it_socket_error_code = (int)info;
        }
        cv_signal(&itp->it_cv);
        mutex_exit(&idm_so_timed_socket_mutex);
        return (NULL);
}

int
idm_so_timed_socket_connect(ksocket_t ks,
   struct sockaddr_storage *sa, int sa_sz, int login_max_usec)
{
        clock_t                 conn_login_max;
        int                     rc, nonblocking, rval;
        idm_so_timed_socket_t   it;
        ksocket_callbacks_t     ks_cb;

        conn_login_max = ddi_get_lbolt() + drv_usectohz(login_max_usec);

        /*
         * Set to non-block socket mode, with callback on connect
         * Early volo used "disconnected" instead of "connectfailed",
         * so set callback to look for both.
         */
        bzero(&it, sizeof (it));
        ks_cb.ksock_cb_flags = KSOCKET_CB_CONNECTED |
            KSOCKET_CB_CONNECTFAILED | KSOCKET_CB_DISCONNECTED;
        ks_cb.ksock_cb_connected =
            (ksocket_callback_t)idm_so_timed_socket_connect_cb;
        ks_cb.ksock_cb_connectfailed =
            (ksocket_callback_t)idm_so_timed_socket_connect_cb;
        ks_cb.ksock_cb_disconnected =
            (ksocket_callback_t)idm_so_timed_socket_connect_cb;
        cv_init(&it.it_cv, NULL, CV_DEFAULT, NULL);
        rc = ksocket_setcallbacks(ks, &ks_cb, (void *)&it, CRED());
        if (rc != 0)
                return (rc);

        /* Set to non-blocking mode */
        nonblocking = 1;
        rc = ksocket_ioctl(ks, FIONBIO, (intptr_t)&nonblocking, &rval,
            CRED());
if (rc != 0) goto cleanup;

        bzero(&it, sizeof (it));
        for (;;) {
                /*
                 * Warning -- in a loopback scenario, the call to
                 * the connect_cb can occur inside the call to
                 * ksocket_connect. Do not hold the mutex around the
                 * call to ksocket_connect.
                 */
                rc = ksocket_connect(ks, (struct sockaddr *)sa, sa_sz, CRED());
                if (rc == 0 || rc == EISCONN) {
                        /* socket success or already success */
                        rc = 0;
                        break;
                }
                if ((rc != EINPROGRESS) && (rc != EALREADY)) {
                        break;
                }

                /* TCP connect still in progress. See if out of time. */
                if (ddi_get_lbolt() > conn_login_max) {
                        /*
                         * Connection retry timeout,
                         * failed connect to target.
                         */
                        rc = ETIMEDOUT;
                        break;
                }

                /*
                 * TCP connect still in progress.  Sleep until callback.
                 * Do NOT go to sleep if the callback already occurred!
                 */
                mutex_enter(&idm_so_timed_socket_mutex);
                if (!it.it_callback_called) {
                        (void) cv_timedwait(&it.it_cv,
                             &idm_so_timed_socket_mutex, conn_login_max);
                }
                if (it.it_callback_called) {
                        rc = it.it_socket_error_code;
                        mutex_exit(&idm_so_timed_socket_mutex);
                        break;
                }
                /* If timer expires, go call ksocket_connect one last time. */
                mutex_exit(&idm_so_timed_socket_mutex);
        }

        /* resume blocking mode */
        nonblocking = 0;
        (void)  ksocket_ioctl(ks, FIONBIO, (intptr_t)&nonblocking, &rval,
            CRED());
cleanup:
        ksocket_setcallbacks(ks, NULL, NULL, CRED());
        cv_destroy(&it.it_cv);
        if (rc != 0) {
                idm_soshutdown(ks);
        }
        return (rc);
}






On 09/01/09 22:21, Peter Cudhea wrote:
Thanks for your comments. I decided to keep the mutex because that is what cv_timedwait knows how to deal with. But I no longer hold the mutex over the call to ksocket_connect. So that should eliminate the possible recursive mutex call that you were worried about.

Here is what I ended up with. I have tested this with successful connects, connects that time out, and connects that are immediately rejected.

I probably don't need the variable "final_pass". If the socket callback is called, then ksocket_connect should then return either success or some definite error. But I felt safer writing that assumption into the code.

/* used by idm_so_timed_socket_connect */
typedef struct idm_so_timed_socket_s {
       kcondvar_t      it_cv;
       boolean_t       it_callback_called;
} idm_so_timed_socket_t;


/* ARGSUSED */
void *
idm_so_timed_socket_connect_cb(ksocket_t ks,
   ksocket_callback_event_t ev, void *arg, uintptr_t info)
{
       idm_so_timed_socket_t   *itp =
               (idm_so_timed_socket_t *)arg;
       ASSERT(itp != NULL);
       ASSERT(ev == KSOCKET_EV_CONNECTED ||
              ev == KSOCKET_EV_CONNECTFAILED ||
              ev == KSOCKET_EV_DISCONNECTED);

       mutex_enter(&idm_so_timed_socket_mutex);
       itp->it_callback_called = B_TRUE;
       cv_signal(&itp->it_cv);
       mutex_exit(&idm_so_timed_socket_mutex);
       return (NULL);
}

int
idm_so_timed_socket_connect(ksocket_t ks,
   struct sockaddr_storage *sa, int sa_sz, int login_max_usec)
{
       clock_t                 conn_login_max, lbolt;
       int                     rc, nonblocking, rval;
       idm_so_timed_socket_t   it;
       ksocket_callbacks_t     ks_cb;
       boolean_t               final_pass = B_FALSE;
conn_login_max = ddi_get_lbolt() + drv_usectohz(login_max_usec);

       /*
         Set to non-block socket mode, with callback on connect
         Early volo used "disconnected" instead of "connectfailed",
         so set callback to look for both.
        */
       bzero(&it, sizeof (it));
ks_cb.ksock_cb_flags = KSOCKET_CB_CONNECTED | KSOCKET_CB_CONNECTFAILED | KSOCKET_CB_DISCONNECTED;
       ks_cb.ksock_cb_connected =
           (ksocket_callback_t)idm_so_timed_socket_connect_cb;
       ks_cb.ksock_cb_connectfailed =
           (ksocket_callback_t)idm_so_timed_socket_connect_cb;
       ks_cb.ksock_cb_disconnected =
           (ksocket_callback_t)idm_so_timed_socket_connect_cb;
       cv_init(&it.it_cv, NULL, CV_DEFAULT, NULL);
       (void) ksocket_setcallbacks(ks, &ks_cb, (void *)&it, CRED());

       /* Set to non-blocking mode */
       nonblocking = 1;
       (void)  ksocket_ioctl(ks, FIONBIO, (intptr_t)&nonblocking, &rval,
           CRED());

       for (;;) {
               /*
                * Warning -- in a loopback scenario, the call to
                * the connect_cb can occur inside the call to
                * ksocket_connect. Do not hold the mutex around the
                * call to ksocket_connect.
                */
rc = ksocket_connect(ks, (struct sockaddr *)sa, sa_sz, CRED());
               if (rc == 0 || rc == EISCONN) {
                       /* socket success or already success */
                       rc = 0;
                       break;
               }
               if ((rc != EINPROGRESS) && (rc != EALREADY)) {
                       break;
               }

               lbolt = ddi_get_lbolt();
               if (lbolt > conn_login_max || final_pass) {
                       /*
                        * Connection retry timeout,
                        * failed connect to target.
                        */
                       rc = ETIMEDOUT;
                       break;
               }
/* TCP connect still in progress. Sleep until callback. */
               mutex_enter(&idm_so_timed_socket_mutex);
               if (! it.it_callback_called) {
                       (void) cv_timedwait(&it.it_cv,
                           &idm_so_timed_socket_mutex, conn_login_max);
               } else {
                       final_pass = B_TRUE;
               }
               mutex_exit(&idm_so_timed_socket_mutex);
       }

       /* resume blocking mode */
       nonblocking = 0;
       (void)  ksocket_ioctl(ks, FIONBIO, (intptr_t)&nonblocking, &rval,
           CRED());
       ksocket_setcallbacks(ks, NULL, NULL, CRED());
       cv_destroy(&it.it_cv);

       return (rc);
}




_______________________________________________
networking-discuss mailing list
[email protected]
_______________________________________________
networking-discuss mailing list
[email protected]

Reply via email to