conntable.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. *
  6. * License: GPL (version 3 or any later version).
  7. * See LICENSE for details.
  8. * END COPYRIGHT BLOCK **/
  9. #ifdef HAVE_CONFIG_H
  10. # include <config.h>
  11. #endif
  12. /* Connection Table */
  13. #include "fe.h"
  14. Connection_Table *
  15. connection_table_new(int table_size)
  16. {
  17. Connection_Table *ct;
  18. int i = 0;
  19. ber_len_t maxbersize = config_get_maxbersize();
  20. ct= (Connection_Table*)slapi_ch_calloc( 1, sizeof(Connection_Table) );
  21. ct->size= table_size;
  22. ct->c = (Connection *)slapi_ch_calloc( 1, table_size * sizeof(Connection) );
  23. ct->fd = (struct POLL_STRUCT *)slapi_ch_calloc(1, table_size * sizeof(struct POLL_STRUCT));
  24. ct->table_mutex = PR_NewLock();
  25. /* We rely on the fact that we called calloc, which zeros the block, so we don't
  26. * init any structure element unless a zero value is troublesome later
  27. */
  28. for ( i = 0; i < table_size; i++ )
  29. {
  30. LBER_SOCKET invalid_socket;
  31. /* DBDB---move this out of here once everything works */
  32. ct->c[i].c_sb = ber_sockbuf_alloc();
  33. invalid_socket = SLAPD_INVALID_SOCKET;
  34. ct->c[i].c_sd = SLAPD_INVALID_SOCKET;
  35. #if defined(USE_OPENLDAP)
  36. ber_sockbuf_ctrl( ct->c[i].c_sb, LBER_SB_OPT_SET_FD, &invalid_socket );
  37. ber_sockbuf_ctrl( ct->c[i].c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &maxbersize );
  38. #else
  39. ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_DESC, &invalid_socket );
  40. /* openldap by default does not use readahead - the implementation is
  41. via a sockbuf_io layer */
  42. ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_NO_READ_AHEAD, LBER_OPT_ON );
  43. ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE, &maxbersize );
  44. #endif /* !USE_OPENLDAP */
  45. /* all connections start out invalid */
  46. ct->fd[i].fd = SLAPD_INVALID_SOCKET;
  47. /* The connection table has a double linked list running through it.
  48. * This is used to find out which connections should be looked at
  49. * in the poll loop. Slot 0 in the table is always the head of
  50. * the linked list. Each slot has a c_next and c_prev which are
  51. * pointers back into the array of connection slots. */
  52. ct->c[i].c_next = NULL;
  53. ct->c[i].c_prev = NULL;
  54. ct->c[i].c_ci= i;
  55. ct->c[i].c_fdi= SLAPD_INVALID_SOCKET_INDEX;
  56. }
  57. return ct;
  58. }
  59. void connection_table_free(Connection_Table *ct)
  60. {
  61. int i;
  62. for (i = 0; i < ct->size; i++)
  63. {
  64. /* Free the contents of the connection structure */
  65. Connection *c= &(ct->c[i]);
  66. connection_done(c);
  67. }
  68. slapi_ch_free((void**)&ct->c);
  69. slapi_ch_free((void**)&ct->fd);
  70. PR_DestroyLock(ct->table_mutex);
  71. slapi_ch_free((void**)&ct);
  72. }
  73. void
  74. connection_table_abandon_all_operations(Connection_Table *ct)
  75. {
  76. int i;
  77. for ( i = 0; i < ct->size; i++ )
  78. {
  79. if ( ct->c[i].c_mutex )
  80. {
  81. PR_EnterMonitor(ct->c[i].c_mutex);
  82. connection_abandon_operations( &ct->c[i] );
  83. PR_ExitMonitor(ct->c[i].c_mutex);
  84. }
  85. }
  86. }
  87. /* Given a file descriptor for a socket, this function will return
  88. * a slot in the connection table to use.
  89. *
  90. * Note: this function is only called from the slapd_daemon (listener)
  91. * thread, which means it will never be called by two threads at
  92. * the same time.
  93. *
  94. * Returns a Connection on success
  95. * Returns NULL on failure
  96. */
  97. Connection *
  98. connection_table_get_connection(Connection_Table *ct, int sd)
  99. {
  100. Connection *c= NULL;
  101. int index, count;
  102. index = sd % ct->size;
  103. for( count = 0; count < ct->size; count++, index = (index + 1) % ct->size)
  104. {
  105. /* Do not use slot 0, slot 0 is head of the list of active connections */
  106. if ( index == 0 )
  107. {
  108. continue;
  109. }
  110. else if( ct->c[index].c_mutex == NULL )
  111. {
  112. break;
  113. }
  114. if( connection_is_free (&(ct->c[index])))
  115. {
  116. break;
  117. }
  118. }
  119. if ( count < ct->size )
  120. {
  121. /* Found an available Connection */
  122. c= &(ct->c[index]);
  123. PR_ASSERT(c->c_next==NULL);
  124. PR_ASSERT(c->c_prev==NULL);
  125. PR_ASSERT(c->c_extension==NULL);
  126. if ( c->c_mutex == NULL )
  127. {
  128. PR_Lock( ct->table_mutex );
  129. c->c_mutex = PR_NewMonitor();
  130. c->c_pdumutex = PR_NewLock();
  131. PR_Unlock( ct->table_mutex );
  132. if ( c->c_mutex == NULL || c->c_pdumutex == NULL )
  133. {
  134. c->c_mutex = NULL;
  135. c->c_pdumutex = NULL;
  136. LDAPDebug( LDAP_DEBUG_ANY,"PR_NewLock failed\n",0, 0, 0 );
  137. exit(1);
  138. }
  139. }
  140. /* Let's make sure there's no cruft left on there from the last time this connection was used. */
  141. /* Note: no need to lock c->c_mutex because this function is only
  142. * called by one thread (the slapd_daemon thread), and if we got this
  143. * far then `c' is not being used by any operation threads, etc.
  144. */
  145. connection_cleanup(c);
  146. #ifdef ENABLE_NUNC_STANS
  147. /* NOTE - ok to do this here even if enable_nunc_stans is off */
  148. c->c_ct = ct; /* pointer to connection table that owns this connection */
  149. #endif
  150. }
  151. else
  152. {
  153. /* couldn't find a Connection */
  154. LDAPDebug( LDAP_DEBUG_CONNS, "max open connections reached\n", 0, 0, 0);
  155. }
  156. return c;
  157. }
  158. /* active connection iteration functions */
  159. Connection*
  160. connection_table_get_first_active_connection (Connection_Table *ct)
  161. {
  162. return ct->c[0].c_next;
  163. }
  164. Connection*
  165. connection_table_get_next_active_connection (Connection_Table *ct, Connection *c)
  166. {
  167. return c->c_next;
  168. }
  169. int connection_table_iterate_active_connections(Connection_Table *ct, void* arg, Connection_Table_Iterate_Function f)
  170. {
  171. /* Locking in this area seems rather undeveloped, I think because typically only one
  172. * thread accesses the connection table (daemon thread). But now we allow other threads
  173. * to iterate over the table. So we use the "new mutex mutex" to lock the table.
  174. */
  175. Connection *current_conn = NULL;
  176. int ret = 0;
  177. PR_Lock(ct->table_mutex);
  178. current_conn = connection_table_get_first_active_connection (ct);
  179. while (current_conn) {
  180. ret = f(current_conn, arg);
  181. if (ret) {
  182. break;
  183. }
  184. current_conn = connection_table_get_next_active_connection (ct, current_conn);
  185. }
  186. PR_Unlock(ct->table_mutex);
  187. return ret;
  188. }
  189. #ifdef FOR_DEBUGGING
  190. static void
  191. connection_table_dump_active_connection (Connection *c)
  192. {
  193. slapi_log_error(SLAPI_LOG_FATAL, "connection", "conn=%p fd=%d refcnt=%d c_flags=%d\n"
  194. "mutex=%p next=%p prev=%p\n\n", c, c->c_sd, c->c_refcnt, c->c_flags,
  195. c->c_mutex, c->c_next, c->c_prev);
  196. }
  197. static void
  198. connection_table_dump_active_connections (Connection_Table *ct)
  199. {
  200. Connection* c;
  201. PR_Lock(ct->table_mutex);
  202. slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** BEGIN DUMP ************\n");
  203. c = connection_table_get_first_active_connection (ct);
  204. while (c)
  205. {
  206. connection_table_dump_active_connection (c);
  207. c = connection_table_get_next_active_connection (ct, c);
  208. }
  209. slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** END DUMP ************\n");
  210. PR_Unlock(ct->table_mutex);
  211. }
  212. #endif
  213. /*
  214. * There's a double linked list of active connections running through the array
  215. * of connections. This function removes a connection (by index) from that
  216. * list. This list is used to find the connections that should be used in the
  217. * poll call.
  218. */
  219. int
  220. connection_table_move_connection_out_of_active_list(Connection_Table *ct,Connection *c)
  221. {
  222. int c_sd; /* for logging */
  223. /* we always have previous element because list contains a dummy header */;
  224. PR_ASSERT (c->c_prev);
  225. #ifdef FOR_DEBUGGING
  226. slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection out of active list\n");
  227. connection_table_dump_active_connection (c);
  228. #endif
  229. c_sd = c->c_sd;
  230. /*
  231. * Note: the connection will NOT be moved off the active list if any other threads still hold
  232. * a reference to the connection (that is, its reference count must be 1 or less).
  233. */
  234. if(c->c_refcnt > 1) {
  235. LDAPDebug2Args(LDAP_DEBUG_CONNS,
  236. "not moving conn %d out of active list because refcnt is %d\n",
  237. c_sd, c->c_refcnt);
  238. return 1; /* failed */
  239. }
  240. /* We need to lock here because we're modifying the linked list */
  241. PR_Lock(ct->table_mutex);
  242. c->c_prev->c_next = c->c_next;
  243. if (c->c_next)
  244. {
  245. c->c_next->c_prev = c->c_prev;
  246. }
  247. connection_release_nolock (c);
  248. connection_cleanup (c);
  249. PR_Unlock(ct->table_mutex);
  250. LDAPDebug1Arg(LDAP_DEBUG_CONNS, "moved conn %d out of active list and freed\n", c_sd);
  251. #ifdef FOR_DEBUGGING
  252. connection_table_dump_active_connections (ct);
  253. #endif
  254. return 0; /* success */
  255. }
  256. /*
  257. * There's a double linked list of active connections running through the array
  258. * of connections. This function adds a connection (by index) to the head of
  259. * that list. This list is used to find the connections that should be used in the
  260. * poll call.
  261. */
  262. void
  263. connection_table_move_connection_on_to_active_list(Connection_Table *ct,Connection *c)
  264. {
  265. PR_ASSERT(c->c_next==NULL);
  266. PR_ASSERT(c->c_prev==NULL);
  267. PR_Lock(ct->table_mutex);
  268. if (connection_acquire_nolock (c)) {
  269. PR_ASSERT(0);
  270. }
  271. #ifdef FOR_DEBUGGING
  272. slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection into active list\n");
  273. connection_table_dump_active_connection (c);
  274. #endif
  275. c->c_next = ct->c[0].c_next;
  276. if ( c->c_next != NULL )
  277. {
  278. c->c_next->c_prev = c;
  279. }
  280. c->c_prev = &(ct->c[0]);
  281. ct->c[0].c_next = c;
  282. PR_Unlock(ct->table_mutex);
  283. #ifdef FOR_DEBUGGING
  284. connection_table_dump_active_connections (ct);
  285. #endif
  286. }
  287. /*
  288. * Replace the following attributes within the entry 'e' with
  289. * information about the connection table:
  290. * connection // multivalued; one value for each active connection
  291. * currentconnections // single valued; an integer count
  292. * totalconnections // single valued; an integer count
  293. * dtablesize // single valued; an integer size
  294. * readwaiters // single valued; an integer count
  295. */
  296. void
  297. connection_table_as_entry(Connection_Table *ct, Slapi_Entry *e)
  298. {
  299. char buf[BUFSIZ];
  300. char maxthreadbuf[BUFSIZ];
  301. struct berval val;
  302. struct berval *vals[2];
  303. int i, nconns, nreadwaiters;
  304. struct tm utm;
  305. vals[0] = &val;
  306. vals[1] = NULL;
  307. attrlist_delete( &e->e_attrs, "connection");
  308. nconns = 0;
  309. nreadwaiters = 0;
  310. for ( i = 0; i < (ct!=NULL?ct->size:0); i++ )
  311. {
  312. PR_Lock( ct->table_mutex );
  313. if ( (ct->c[i].c_mutex == NULL) || (ct->c[i].c_mutex == (PRMonitor*)-1) )
  314. {
  315. PR_Unlock( ct->table_mutex );
  316. continue;
  317. }
  318. /* Can't take c_mutex if holding table_mutex; temporarily unlock */
  319. PR_Unlock( ct->table_mutex );
  320. PR_EnterMonitor(ct->c[i].c_mutex);
  321. if ( ct->c[i].c_sd != SLAPD_INVALID_SOCKET )
  322. {
  323. char buf2[20];
  324. int lendn = ct->c[i].c_dn ? strlen(ct->c[i].c_dn) : 6; /* "NULLDN" */
  325. char *bufptr = &buf[0];
  326. char *newbuf = NULL;
  327. int maxthreadstate = 0;
  328. if(ct->c[i].c_flags & CONN_FLAG_MAX_THREADS){
  329. maxthreadstate = 1;
  330. }
  331. nconns++;
  332. if ( ct->c[i].c_gettingber )
  333. {
  334. nreadwaiters++;
  335. }
  336. gmtime_r( &ct->c[i].c_starttime, &utm );
  337. strftime( buf2, sizeof(buf2), "%Y%m%d%H%M%SZ", &utm );
  338. /*
  339. * Max threads per connection stats
  340. *
  341. * Appended output "1:2:3"
  342. *
  343. * 1 = Connection max threads state: 1 is in max threads, 0 is not
  344. * 2 = The number of times this thread has hit max threads
  345. * 3 = The number of operations attempted that were blocked
  346. * by max threads.
  347. */
  348. PR_snprintf(maxthreadbuf, sizeof(maxthreadbuf), "%d:%"NSPRIu64":%"NSPRIu64"",
  349. maxthreadstate, ct->c[i].c_maxthreadscount,
  350. ct->c[i].c_maxthreadsblocked);
  351. if ((lendn + strlen(maxthreadbuf)) > (BUFSIZ - 46)) {
  352. /*
  353. * 46 = 4 for the "i" couter + 20 for buf2 +
  354. * 10 for c_opsinitiated + 10 for c_opscompleted +
  355. * 1 for c_gettingber + 1
  356. */
  357. newbuf = (char *) slapi_ch_malloc(lendn + strlen(maxthreadbuf) + 46);
  358. bufptr = newbuf;
  359. }
  360. sprintf( bufptr, "%d:%s:%d:%d:%s%s:%s:%s", i,
  361. buf2,
  362. ct->c[i].c_opsinitiated,
  363. ct->c[i].c_opscompleted,
  364. ct->c[i].c_gettingber ? "r" : "-",
  365. "",
  366. ct->c[i].c_dn ? ct->c[i].c_dn : "NULLDN",
  367. maxthreadbuf
  368. );
  369. val.bv_val = bufptr;
  370. val.bv_len = strlen( bufptr );
  371. attrlist_merge( &e->e_attrs, "connection", vals );
  372. slapi_ch_free_string(&newbuf);
  373. }
  374. PR_ExitMonitor(ct->c[i].c_mutex);
  375. }
  376. PR_snprintf( buf, sizeof(buf), "%d", nconns );
  377. val.bv_val = buf;
  378. val.bv_len = strlen( buf );
  379. attrlist_replace( &e->e_attrs, "currentconnections", vals );
  380. PR_snprintf( buf, sizeof(buf), "%" NSPRIu64, slapi_counter_get_value(num_conns));
  381. val.bv_val = buf;
  382. val.bv_len = strlen( buf );
  383. attrlist_replace( &e->e_attrs, "totalconnections", vals );
  384. PR_snprintf( buf, sizeof(buf), "%" NSPRIu64, slapi_counter_get_value(conns_in_maxthreads));
  385. val.bv_val = buf;
  386. val.bv_len = strlen( buf );
  387. attrlist_replace( &e->e_attrs, "currentconnectionsatmaxthreads", vals );
  388. PR_snprintf( buf, sizeof(buf), "%" NSPRIu64, slapi_counter_get_value(max_threads_count));
  389. val.bv_val = buf;
  390. val.bv_len = strlen( buf );
  391. attrlist_replace( &e->e_attrs, "maxthreadsperconnhits", vals );
  392. PR_snprintf( buf, sizeof(buf), "%d", (ct!=NULL?ct->size:0) );
  393. val.bv_val = buf;
  394. val.bv_len = strlen( buf );
  395. attrlist_replace( &e->e_attrs, "dtablesize", vals );
  396. PR_snprintf( buf, sizeof(buf), "%d", nreadwaiters );
  397. val.bv_val = buf;
  398. val.bv_len = strlen( buf );
  399. attrlist_replace( &e->e_attrs, "readwaiters", vals );
  400. }
  401. void
  402. connection_table_dump_activity_to_errors_log(Connection_Table *ct)
  403. {
  404. int i;
  405. for ( i = 0; i < ct->size; i++ )
  406. {
  407. Connection *c= &(ct->c[i]);
  408. if ( c->c_mutex )
  409. {
  410. /* Find the connection we are referring to */
  411. int j= c->c_fdi;
  412. PR_EnterMonitor(c->c_mutex);
  413. if ( (c->c_sd != SLAPD_INVALID_SOCKET) &&
  414. (j >= 0) && (c->c_prfd == ct->fd[j].fd) )
  415. {
  416. int r = ct->fd[j].out_flags & SLAPD_POLL_FLAGS;
  417. if ( r )
  418. {
  419. LDAPDebug( LDAP_DEBUG_CONNS,"activity on %d%s\n", i, r ? "r" : "",0 );
  420. }
  421. }
  422. PR_ExitMonitor(c->c_mutex);
  423. }
  424. }
  425. }
  426. #if 0
  427. void dump_op_list(FILE *file, Operation *op);
  428. void
  429. connection_table_dump(Connection_Table *ct)
  430. {
  431. FILE *file;
  432. file = fopen("/tmp/slapd.conn", "a+");
  433. if (file != NULL)
  434. {
  435. int i;
  436. fprintf(file, "=============pid=%d==================\n", getpid());
  437. for ( i = 0; i < ct->size; i++ )
  438. {
  439. if ( (ct->c[i].c_sd == SLAPD_INVALID_SOCKET) && (ct->c[i].c_connid == 0) )
  440. {
  441. continue;
  442. }
  443. fprintf(file, "c[%d].c_dn=0x%x\n", i, ct->c[i].c_dn);
  444. dump_op_list(file, ct->c[i].c_ops);
  445. fprintf(file, "c[%d].c_sb.sb_sd=%d\n", i, ct->c[i].c_sd);
  446. fprintf(file, "c[%d].c_connid=%d\n", i, ct->c[i].c_connid);
  447. fprintf(file, "c[%d].c_opsinitiated=%d\n", i, ct->c[i].c_opsinitiated);
  448. fprintf(file, "c[%d].c_opscompleted=%d\n", i, ct->c[i].c_opscompleted);
  449. }
  450. fclose(file);
  451. }
  452. }
  453. static const char *
  454. op_status2str( int o_status )
  455. {
  456. const char *s = "unknown";
  457. switch( o_status ) {
  458. case SLAPI_OP_STATUS_PROCESSING:
  459. s = "processing";
  460. break;
  461. case SLAPI_OP_STATUS_ABANDONED:
  462. s = "abandoned";
  463. break;
  464. case SLAPI_OP_STATUS_WILL_COMPLETE:
  465. s = "will_complete";
  466. break;
  467. case SLAPI_OP_STATUS_RESULT_SENT:
  468. s = "result_sent";
  469. break;
  470. }
  471. return s;
  472. }
  473. void
  474. dump_op_list(FILE *file, Operation *op)
  475. {
  476. Operation *tmp;
  477. for ( tmp = op; tmp != NULL; tmp = tmp->o_next )
  478. {
  479. fprintf(file,
  480. "(o_msgid=%d, o_tag=%d, o_sdn=0x%x, o_opid=%d, o_connid=%d, o_status=%s)\n",
  481. tmp->o_msgid, tmp->o_tag, slapi_sdn_get_dn(&tmp->o_sdn), tmp->o_connid,
  482. *tmp->o_tid, op_status2str( tmp->o_status ));
  483. }
  484. }
  485. #endif /* 0 */