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]