XRootD
XrdLinkCtl.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d L i n k C t l . c c */
4 /* */
5 /* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* Produced by Andrew Hanushevsky for Stanford University under contract */
7 /* DE-AC02-76-SFO0515 with the Department of Energy */
8 /* */
9 /* This file is part of the XRootD software suite. */
10 /* */
11 /* XRootD is free software: you can redistribute it and/or modify it under */
12 /* the terms of the GNU Lesser General Public License as published by the */
13 /* Free Software Foundation, either version 3 of the License, or (at your */
14 /* option) any later version. */
15 /* */
16 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19 /* License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24 /* */
25 /* The copyright holder's institutional names and contributor's names may not */
26 /* be used to endorse or promote products derived from this software without */
27 /* specific prior written permission of the institution or contributor. */
28 /******************************************************************************/
29 
30 #include <sys/types.h>
31 #include <fcntl.h>
32 #include <ctime>
33 
34 #include "Xrd/XrdInet.hh"
35 #include "Xrd/XrdLinkCtl.hh"
36 #include "Xrd/XrdLinkMatch.hh"
37 #include "Xrd/XrdPoll.hh"
38 #include "Xrd/XrdScheduler.hh"
39 
40 #define TRACELINK this
41 #include "Xrd/XrdTrace.hh"
42 
43 #include "XrdSys/XrdSysAtomics.hh"
44 #include "XrdSys/XrdSysLogger.hh"
45 #include "XrdSys/XrdSysPthread.hh"
46 
47 /******************************************************************************/
48 /* G l o b a l O b j e c t s */
49 /******************************************************************************/
50 
51 namespace XrdGlobal
52 {
53 extern XrdSysError Log;
55 extern XrdInet *XrdNetTCP;
56 };
57 
58 using namespace XrdGlobal;
59 
60 /******************************************************************************/
61 /* S t a t i c s */
62 /******************************************************************************/
63 
64  XrdLinkCtl **XrdLinkCtl::LinkTab = 0;
65  char *XrdLinkCtl::LinkBat = 0;
66  // Compute the number of link objects we should allocate at a time.
67  // Generally, we like to allocate 8k of them at a time but always
68  // as a power of two.
69  const unsigned int XrdLinkCtl::LinkAlloc = [] {
70  unsigned i = 8192 / sizeof(XrdLink);
71  unsigned j = 1;
72  while (i >>= 1) j <<= 1;
73  return j;
74  } ();
75  int XrdLinkCtl::LTLast = -1;
76  int XrdLinkCtl::maxFD = 0;
77  XrdSysMutex XrdLinkCtl::LTMutex;
78  short XrdLinkCtl::killWait = 3; // Kill then wait;
79  short XrdLinkCtl::waitKill = 4; // Wait then kill
80 
81  const char *XrdLinkCtl::TraceID = "LinkCtl";
82 
83 namespace
84 {
85  XrdSysMutex instMutex;
86  unsigned int myInstance = 1;
87  int idleCheck;
88  int idleTicks;
89 
90 static const int XRDLINK_USED = 0x01;
91 static const int XRDLINK_FREE = 0x00;
92 
93 class LinkScan : public XrdJob
94 {
95 public:
96 
97 void DoIt() {XrdLinkCtl::idleScan();
98  Sched.Schedule((XrdJob *)this, idleCheck+time(0));
99  }
100  LinkScan() : XrdJob("Idle link scan") {}
101  ~LinkScan() {}
102 };
103 }
104 
105 /******************************************************************************/
106 /* A l l o c */
107 /******************************************************************************/
108 
110 {
111  XrdLinkCtl *lp;
112  char hName[1024], *unp, buff[32];
113  int bl, peerFD = peer.SockFD();
114 
115 // Make sure that the incoming file descriptor can be handled
116 //
117  if (peerFD < 0 || peerFD >= maxFD)
118  {snprintf(hName, sizeof(hName), "%d", peerFD);
119  Log.Emsg("Link", "attempt to alloc out of range FD -",hName);
120  return (XrdLink *)0;
121  }
122 
123 // Make sure that the link slot is available
124 //
125  LTMutex.Lock();
126  if (LinkBat[peerFD])
127  {LTMutex.UnLock();
128  snprintf(hName, sizeof(hName), "%d", peerFD);
129  Log.Emsg("Link", "attempt to reuse active link FD -",hName);
130  return (XrdLink *)0;
131  }
132 
133 // Check if we already have a link object in this slot. If not, allocate
134 // a quantum of link objects and put them in the table.
135 //
136  if (!(lp = LinkTab[peerFD]))
137  {unsigned int i;
138  XrdLinkCtl **blp, *nlp = new XrdLinkCtl[LinkAlloc]();
139  if (!nlp)
140  {LTMutex.UnLock();
141  Log.Emsg("Link", ENOMEM, "create link");
142  return (XrdLink *)0;
143  }
144  blp = &LinkTab[peerFD/LinkAlloc*LinkAlloc];
145  for (i = 0; i < LinkAlloc; i++, blp++) *blp = &nlp[i];
146  lp = LinkTab[peerFD];
147  }
148  else lp->Reset();
149  LinkBat[peerFD] = XRDLINK_USED;
150  if (peerFD > LTLast) LTLast = peerFD;
151  LTMutex.UnLock();
152 
153 // Establish the instance number of this link. This is will prevent us from
154 // sending asynchronous responses to the wrong client when the file descriptor
155 // gets reused for connections to the same host.
156 //
157  instMutex.Lock();
158  lp->Instance = myInstance++;
159  instMutex.UnLock();
160 
161 // Establish the address and connection name of this link
162 //
163  peer.Format(hName, sizeof(hName), XrdNetAddr::fmtAuto,
165  lp->HostName = strdup(hName);
166  lp->HNlen = strlen(hName);
167  XrdNetTCP->Trim(hName);
168  lp->Addr = peer;
169  strlcpy(lp->Lname, hName, sizeof(lp->Lname));
170  bl = sprintf(buff, "anon.0:%d", peerFD);
171  unp = lp->Uname + sizeof(Uname) - bl - 1; // Solaris compatibility
172  memcpy(unp, buff, bl);
173  lp->ID = unp;
174  lp->PollInfo.FD = lp->LinkInfo.FD = peerFD;
175  lp->Comment = (const char *)unp;
176 
177 // Set options as needed
178 //
179  lp->LockReads = (0 != (opts & XRDLINK_RDLOCK));
180  lp->KeepFD = (0 != (opts & XRDLINK_NOCLOSE));
181 
182 // Update statistics and return the link. We need to actually get the stats
183 // mutex even when using atomics because we need to use compound operations.
184 // The atomics will keep reporters from seeing partial results.
185 //
186  statsMutex.Lock();
187  AtomicInc(LinkCountTot); // LinkCountTot++
188  if (LinkCountMax <= AtomicInc(LinkCount)) LinkCountMax = LinkCount;
189  statsMutex.UnLock();
190  return lp;
191 }
192 
193 /******************************************************************************/
194 /* F i n d */
195 /******************************************************************************/
196 
197 // Warning: curr must be set to a value of 0 or less on the initial call and
198 // not touched therafter unless a null pointer is returned. When an
199 // actual link object pointer is returned, it's refcount is increased.
200 // The count is automatically decreased on the next call to Find().
201 //
203 {
204  XrdLinkCtl *lp;
205  const int MaxSeek = 16;
206  unsigned int myINS;
207  int i, seeklim = MaxSeek;
208 
209 // Do initialization
210 //
211  LTMutex.Lock();
212  if (curr >= 0 && LinkTab[curr]) LinkTab[curr]->setRef(-1);
213  else curr = -1;
214 
215 // Find next matching link. Since this may take some time, we periodically
216 // release the LTMutex lock which drives up overhead but will still allow
217 // other critical operations to occur.
218 //
219  for (i = curr+1; i <= LTLast; i++)
220  {if ((lp = LinkTab[i]) && LinkBat[i] && lp->HostName)
221  if (!who
222  || who->Match(lp->ID,lp->Lname-lp->ID-1,lp->HostName,lp->HNlen))
223  {myINS = lp->Instance;
224  LTMutex.UnLock();
225  lp->setRef(1);
226  curr = i;
227  if (myINS == lp->Instance) return lp;
228  LTMutex.Lock();
229  }
230  if (!seeklim--) {LTMutex.UnLock(); seeklim = MaxSeek; LTMutex.Lock();}
231  }
232 
233 // Done scanning the table
234 //
235  LTMutex.UnLock();
236  curr = -1;
237  return 0;
238 }
239 
240 /******************************************************************************/
241 /* g e t N a m e */
242 /******************************************************************************/
243 
244 // Warning: curr must be set to a value of 0 or less on the initial call and
245 // not touched therafter unless null is returned. Returns the length
246 // the name in nbuf.
247 //
248 int XrdLinkCtl::getName(int &curr, char *nbuf, int nbsz, XrdLinkMatch *who)
249 {
250  XrdLinkCtl *lp;
251  const int MaxSeek = 16;
252  int i, ulen = 0, seeklim = MaxSeek;
253 
254 // Find next matching link. Since this may take some time, we periodically
255 // release the LTMutex lock which drives up overhead but will still allow
256 // other critical operations to occur.
257 //
258  LTMutex.Lock();
259  for (i = curr+1; i <= LTLast; i++)
260  {if ((lp = LinkTab[i]) && LinkBat[i] && lp->HostName)
261  if (!who
262  || who->Match(lp->ID,lp->Lname-lp->ID-1,lp->HostName,lp->HNlen))
263  {ulen = lp->Client(nbuf, nbsz);
264  LTMutex.UnLock();
265  curr = i;
266  return ulen;
267  }
268  if (!seeklim--) {LTMutex.UnLock(); seeklim = MaxSeek; LTMutex.Lock();}
269  }
270  LTMutex.UnLock();
271 
272 // Done scanning the table
273 //
274  curr = -1;
275  return 0;
276 }
277 
278 /******************************************************************************/
279 /* i d l e S c a n */
280 /******************************************************************************/
281 
282 #undef TRACELINK
283 #define TRACELINK lp
284 
286 {
287  XrdLinkCtl *lp;
288  int i, ltlast, lnum = 0, tmo = 0, tmod = 0;
289 
290 // Get the current link high watermark
291 //
292  LTMutex.Lock();
293  ltlast = LTLast;
294  LTMutex.UnLock();
295 
296 // Scan across all links looking for idle links. Links are never deallocated
297 // so we don't need any special kind of lock for these
298 //
299  for (i = 0; i <= ltlast; i++)
300  {if (LinkBat[i] != XRDLINK_USED
301  || !(lp = LinkTab[i])) continue;
302  lnum++;
303  lp->LinkInfo.opMutex.Lock();
304  if (lp->isIdle) tmo++;
305  lp->isIdle++;
306  if ((int(lp->isIdle)) < idleTicks)
307  {lp->LinkInfo.opMutex.UnLock(); continue;}
308  lp->isIdle = 0;
309  if (!(lp->PollInfo.Poller) || !(lp->PollInfo.isEnabled))
310  Log.Emsg("LinkScan","Link",lp->ID,"is disabled and idle.");
311  else if (lp->LinkInfo.InUse == 1)
312  {lp->PollInfo.Poller->Disable(lp->PollInfo, "idle timeout");
313  tmod++;
314  }
315  lp->LinkInfo.opMutex.UnLock();
316  }
317 
318 // Trace what we did
319 //
320  TRACE(CONN, lnum <<" links; " <<tmo <<" idle; " <<tmod <<" force closed");
321 }
322 
323 /******************************************************************************/
324 /* s e t K W T */
325 /******************************************************************************/
326 
327 void XrdLinkCtl::setKWT(int wkSec, int kwSec)
328 {
329  if (wkSec > 0) waitKill = static_cast<short>(wkSec);
330  if (kwSec > 0) killWait = static_cast<short>(kwSec);
331 }
332 
333 /******************************************************************************/
334 /* S e t u p */
335 /******************************************************************************/
336 
337 int XrdLinkCtl::Setup(int maxfds, int idlewait)
338 {
339  maxFD = maxfds;
340  TRACE(DEBUG, "Allocating " <<LinkAlloc <<" link objects at a time");
341 
342 // Create the link table
343 //
344  if (!(LinkTab = (XrdLinkCtl **)malloc(maxfds*sizeof(XrdLinkCtl*)+LinkAlloc)))
345  {Log.Emsg("Link", ENOMEM, "create LinkTab"); return 0;}
346  memset((void *)LinkTab, 0, maxfds*sizeof(XrdLinkCtl *));
347 
348 // Create the slot status table
349 //
350  if (!(LinkBat = (char *)malloc(maxfds*sizeof(char)+LinkAlloc)))
351  {Log.Emsg("Link", ENOMEM, "create LinkBat"); return 0;}
352  memset((void *)LinkBat, XRDLINK_FREE, maxfds*sizeof(char));
353 
354 // Create an idle connection scan job
355 //
356  if (idlewait)
357  {if ((idleCheck = idlewait/3)) idleTicks = 3;
358  else {idleTicks = 1;
359  idleCheck = idlewait;
360  }
361  LinkScan *ls = new LinkScan;
362  Sched.Schedule((XrdJob *)ls, idleCheck+time(0));
363  }
364 
365 // All done
366 //
367  return 1;
368 }
369 
370 /******************************************************************************/
371 /* S y n c A l l */
372 /******************************************************************************/
373 
375 {
376  int myLTLast;
377 
378 // Get the current last entry
379 //
380  LTMutex.Lock(); myLTLast = LTLast; LTMutex.UnLock();
381 
382 // Run through all the links and sync the statistics
383 //
384  for (int i = 0; i <= myLTLast; i++)
385  {if (LinkBat[i] == XRDLINK_USED && LinkTab[i]) LinkTab[i]->syncStats();}
386 }
387 
388 /******************************************************************************/
389 /* U n h o o k */
390 /******************************************************************************/
391 
392 void XrdLinkCtl::Unhook(int fd)
393 {
394 
395 // Indicate link no longer actvely neing used
396 //
397  LTMutex.Lock();
398  LinkBat[fd] = XRDLINK_FREE;
399  if (fd == LTLast) while(LTLast && !(LinkBat[LTLast])) LTLast--;
400  LTMutex.UnLock();
401 }
int DoIt(int argpnt, int argc, char **argv, bool singleshot)
Definition: XrdAccTest.cc:262
#define DEBUG(x)
Definition: XrdBwmTrace.hh:54
#define XRDLINK_NOCLOSE
Definition: XrdLinkCtl.hh:59
#define XRDLINK_RDLOCK
Definition: XrdLinkCtl.hh:58
struct myOpts opts
#define AtomicInc(x)
size_t strlcpy(char *dst, const char *src, size_t sz)
#define TRACE(act, x)
Definition: XrdTrace.hh:63
Definition: XrdJob.hh:43
const char * Comment
Definition: XrdJob.hh:47
static XrdLink * Alloc(XrdNetAddr &peer, int opts=0)
Definition: XrdLinkCtl.cc:109
static void SyncAll()
Synchronize statustics for ll links.
Definition: XrdLinkCtl.cc:374
static short waitKill
Definition: XrdLinkCtl.hh:191
static int Setup(int maxfds, int idlewt)
Definition: XrdLinkCtl.cc:337
static XrdLink * Find(int &curr, XrdLinkMatch *who=0)
Definition: XrdLinkCtl.cc:202
static void setKWT(int wkSec, int kwSec)
Definition: XrdLinkCtl.cc:327
static void idleScan()
Look for idle links and close hem down.
Definition: XrdLinkCtl.cc:285
static short killWait
Link destruction control constants.
Definition: XrdLinkCtl.hh:190
static int getName(int &curr, char *bname, int blen, XrdLinkMatch *who=0)
Definition: XrdLinkCtl.cc:248
static void Unhook(int fd)
Unhook a link from the active table of links.
Definition: XrdLinkCtl.cc:392
XrdSysRecMutex opMutex
Definition: XrdLinkInfo.hh:46
int Match(const char *uname, int unlen, const char *hname, int hnlen)
Definition: XrdLinkMatch.cc:39
int Client(char *buff, int blen)
Definition: XrdLinkXeq.cc:152
char Uname[24]
Definition: XrdLinkXeq.hh:200
XrdLinkInfo LinkInfo
Definition: XrdLinkXeq.hh:144
XrdNetAddr Addr
Definition: XrdLinkXeq.hh:192
void Reset()
Definition: XrdLinkXeq.cc:113
XrdPollInfo PollInfo
Definition: XrdLinkXeq.hh:145
bool LockReads
Definition: XrdLinkXeq.hh:197
char Lname[256]
Definition: XrdLinkXeq.hh:201
static const int noPort
Do not add port number.
static const int old6Map4
Use deprecated IPV6 mapped format.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtAuto
Hostname if already resolved o/w use fmtAddr.
void Trim(char *hname)
Definition: XrdNet.cc:343
bool isEnabled
Definition: XrdPollInfo.hh:46
XrdPoll * Poller
Definition: XrdPollInfo.hh:43
virtual void Disable(XrdPollInfo &pInfo, const char *etxt=0)=0
void Schedule(XrdJob *jp)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
XrdInet * XrdNetTCP
Definition: XrdGlobals.cc:53
XrdSysError Log
Definition: XrdConfig.cc:112
XrdScheduler Sched
Definition: XrdLinkCtl.cc:54