Whamcloud - gitweb
LU-56 lnet: match-table for Portals
[fs/lustre-release.git] / lnet / lnet / lib-ptl.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 021110-1307, USA
20  *
21  * GPL HEADER END
22  */
23 /*
24  * Copyright (c) 2011, 2012, Whamcloud, Inc.
25  */
26 /*
27  * This file is part of Lustre, http://www.lustre.org/
28  * Lustre is a trademark of Sun Microsystems, Inc.
29  *
30  * lnet/lnet/lib-ptl.c
31  *
32  * portal & match routines
33  *
34  * Author: liang@whamcloud.com
35  */
36
37 #define DEBUG_SUBSYSTEM S_LNET
38
39 #include <lnet/lib-lnet.h>
40
41 static int
42 lnet_ptl_match_type(unsigned int index, lnet_process_id_t match_id,
43                     __u64 mbits, __u64 ignore_bits)
44 {
45         struct lnet_portal      *ptl = the_lnet.ln_portals[index];
46         int                     unique;
47
48         unique = ignore_bits == 0 &&
49                  match_id.nid != LNET_NID_ANY &&
50                  match_id.pid != LNET_PID_ANY;
51
52         LASSERT(!lnet_ptl_is_unique(ptl) || !lnet_ptl_is_wildcard(ptl));
53
54         /* prefer to check w/o any lock */
55         if (likely(lnet_ptl_is_unique(ptl) || lnet_ptl_is_wildcard(ptl)))
56                 goto match;
57
58         /* unset, new portal */
59         LNET_LOCK();
60         /* check again with lock */
61         if (unlikely(lnet_ptl_is_unique(ptl) || lnet_ptl_is_wildcard(ptl))) {
62                 LNET_UNLOCK();
63                 goto match;
64         }
65
66         /* still not set */
67         if (unique)
68                 lnet_ptl_setopt(ptl, LNET_PTL_MATCH_UNIQUE);
69         else
70                 lnet_ptl_setopt(ptl, LNET_PTL_MATCH_WILDCARD);
71
72         LNET_UNLOCK();
73
74         return 1;
75
76  match:
77         if ((lnet_ptl_is_unique(ptl) && !unique) ||
78             (lnet_ptl_is_wildcard(ptl) && unique))
79                 return 0;
80         return 1;
81 }
82
83 static int
84 lnet_try_match_md(int index, int op_mask, lnet_process_id_t src,
85                   unsigned int rlength, unsigned int roffset,
86                   __u64 match_bits, lnet_libmd_t *md, lnet_msg_t *msg)
87 {
88         /* ALWAYS called holding the LNET_LOCK, and can't LNET_UNLOCK;
89          * lnet_match_blocked_msg() relies on this to avoid races */
90         unsigned int    offset;
91         unsigned int    mlength;
92         lnet_me_t       *me = md->md_me;
93
94         /* mismatched MD op */
95         if ((md->md_options & op_mask) == 0)
96                 return LNET_MATCHMD_NONE;
97
98         /* MD exhausted */
99         if (lnet_md_exhausted(md))
100                 return LNET_MATCHMD_NONE;
101
102         /* mismatched ME nid/pid? */
103         if (me->me_match_id.nid != LNET_NID_ANY &&
104             me->me_match_id.nid != src.nid)
105                 return LNET_MATCHMD_NONE;
106
107         if (me->me_match_id.pid != LNET_PID_ANY &&
108             me->me_match_id.pid != src.pid)
109                 return LNET_MATCHMD_NONE;
110
111         /* mismatched ME matchbits? */
112         if (((me->me_match_bits ^ match_bits) & ~me->me_ignore_bits) != 0)
113                 return LNET_MATCHMD_NONE;
114
115         /* Hurrah! This _is_ a match; check it out... */
116
117         if ((md->md_options & LNET_MD_MANAGE_REMOTE) == 0)
118                 offset = md->md_offset;
119         else
120                 offset = roffset;
121
122         if ((md->md_options & LNET_MD_MAX_SIZE) != 0) {
123                 mlength = md->md_max_size;
124                 LASSERT(md->md_offset + mlength <= md->md_length);
125         } else {
126                 mlength = md->md_length - offset;
127         }
128
129         if (rlength <= mlength) {        /* fits in allowed space */
130                 mlength = rlength;
131         } else if ((md->md_options & LNET_MD_TRUNCATE) == 0) {
132                 /* this packet _really_ is too big */
133                 CERROR("Matching packet from %s, match "LPU64
134                        " length %d too big: %d left, %d allowed\n",
135                        libcfs_id2str(src), match_bits, rlength,
136                        md->md_length - offset, mlength);
137
138                 return LNET_MATCHMD_DROP;
139         }
140
141         /* Commit to this ME/MD */
142         CDEBUG(D_NET, "Incoming %s index %x from %s of "
143                "length %d/%d into md "LPX64" [%d] + %d\n",
144                (op_mask == LNET_MD_OP_PUT) ? "put" : "get",
145                index, libcfs_id2str(src), mlength, rlength,
146                md->md_lh.lh_cookie, md->md_niov, offset);
147
148         lnet_msg_attach_md(msg, md, offset, mlength);
149         md->md_offset = offset + mlength;
150
151         /* Auto-unlink NOW, so the ME gets unlinked if required.
152          * We bumped md->md_refcount above so the MD just gets flagged
153          * for unlink when it is finalized. */
154         if ((md->md_flags & LNET_MD_FLAG_AUTO_UNLINK) != 0 &&
155             lnet_md_exhausted(md)) {
156                 lnet_md_unlink(md);
157         }
158
159         return LNET_MATCHMD_OK;
160 }
161
162 struct lnet_match_table *
163 lnet_mt_of_attach(unsigned int index, lnet_process_id_t id,
164                   __u64 mbits, __u64 ignore_bits, lnet_ins_pos_t pos)
165 {
166         struct lnet_portal *ptl;
167
168         LASSERT(index < the_lnet.ln_nportals);
169
170         if (!lnet_ptl_match_type(index, id, mbits, ignore_bits))
171                 return NULL;
172
173         ptl = the_lnet.ln_portals[index];
174         /* NB: Now we only have one match-table for each portal,
175          * and will have match-table per CPT in upcoming changes,
176          * ME will be scattered to different match-tables based
177          * on attaching information */
178         return ptl->ptl_mtable;
179 }
180
181 struct lnet_match_table *
182 lnet_mt_of_match(unsigned int index, lnet_process_id_t id, __u64 mbits)
183 {
184         struct lnet_portal *ptl;
185
186         LASSERT(index < the_lnet.ln_nportals);
187
188         ptl = the_lnet.ln_portals[index];
189         if (!lnet_ptl_is_unique(ptl) &&
190             !lnet_ptl_is_wildcard(ptl) && !lnet_ptl_is_lazy(ptl))
191                 return NULL;
192
193         /* NB: Now we only have one match-table for each portal,
194          * and will have match-table per CPT in upcoming changes,
195          * request will be scattered to different match-tables based
196          * on matching information */
197         return ptl->ptl_mtable;
198 }
199
200 cfs_list_t *
201 lnet_mt_match_head(struct lnet_match_table *mtable,
202                    lnet_process_id_t id, __u64 mbits)
203 {
204         struct lnet_portal *ptl = the_lnet.ln_portals[mtable->mt_portal];
205
206         if (lnet_ptl_is_wildcard(ptl)) {
207                 return &mtable->mt_mlist;
208
209         } else if (lnet_ptl_is_unique(ptl)) {
210                 unsigned long hash = mbits + id.nid + id.pid;
211
212                 hash = cfs_hash_long(hash, LNET_MT_HASH_BITS);
213                 return &mtable->mt_mhash[hash];
214         }
215
216         return NULL;
217 }
218
219 int
220 lnet_mt_match_md(struct lnet_match_table *mtable,
221                  int op_mask, lnet_process_id_t src,
222                  unsigned int rlength, unsigned int roffset,
223                  __u64 match_bits, lnet_msg_t *msg)
224 {
225         cfs_list_t              *head;
226         lnet_me_t               *me;
227         lnet_me_t               *tmp;
228         int                     rc;
229
230         head = lnet_mt_match_head(mtable, src, match_bits);
231         if (head == NULL) /* nobody posted anything on this portal */
232                 goto out;
233
234         cfs_list_for_each_entry_safe(me, tmp, head, me_list) {
235                 /* ME attached but MD not attached yet */
236                 if (me->me_md == NULL)
237                         continue;
238
239                 LASSERT(me == me->me_md->md_me);
240
241                 rc = lnet_try_match_md(mtable->mt_portal,
242                                        op_mask, src, rlength, roffset,
243                                        match_bits, me->me_md, msg);
244                 switch (rc) {
245                 default:
246                         LBUG();
247
248                 case LNET_MATCHMD_NONE:
249                         continue;
250
251                 case LNET_MATCHMD_OK:
252                         return LNET_MATCHMD_OK;
253
254                 case LNET_MATCHMD_DROP:
255                         return LNET_MATCHMD_DROP;
256                 }
257                 /* not reached */
258         }
259
260  out:
261         if (op_mask == LNET_MD_OP_GET ||
262             !lnet_ptl_is_lazy(the_lnet.ln_portals[mtable->mt_portal]))
263                 return LNET_MATCHMD_DROP;
264
265         return LNET_MATCHMD_NONE;
266 }
267
268 int
269 lnet_ptl_match_md(unsigned int index, int op_mask, lnet_process_id_t src,
270                   unsigned int rlength, unsigned int roffset,
271                   __u64 match_bits, lnet_msg_t *msg)
272 {
273         struct lnet_match_table *mtable;
274         struct lnet_portal      *ptl;
275         int                     rc;
276
277         CDEBUG(D_NET, "Request from %s of length %d into portal %d "
278                "MB="LPX64"\n", libcfs_id2str(src), rlength, index, match_bits);
279
280         if (index >= the_lnet.ln_nportals) {
281                 CERROR("Invalid portal %d not in [0-%d]\n",
282                        index, the_lnet.ln_nportals);
283                 return LNET_MATCHMD_DROP;
284         }
285
286         mtable = lnet_mt_of_match(index, src, match_bits);
287         if (mtable == NULL) {
288                 CDEBUG(D_NET, "Drop early message from %s of length %d into "
289                               "portal %d MB="LPX64"\n",
290                               libcfs_id2str(src), rlength, index, match_bits);
291                 return LNET_MATCHMD_DROP;
292         }
293
294         ptl = the_lnet.ln_portals[index];
295         LNET_LOCK();
296
297         if (the_lnet.ln_shutdown) {
298                 rc =  LNET_MATCHMD_DROP;
299                 goto out;
300         }
301
302         rc = lnet_mt_match_md(mtable, op_mask, src, rlength,
303                               roffset, match_bits, msg);
304         if (rc != LNET_MATCHMD_NONE) /* matched or dropping */
305                 goto out;
306
307         if (!msg->msg_rx_ready_delay)
308                 goto out;
309
310         LASSERT(!msg->msg_rx_delayed);
311         msg->msg_rx_delayed = 1;
312         cfs_list_add_tail(&msg->msg_list, &ptl->ptl_msgq);
313
314         CDEBUG(D_NET,
315                "Delaying %s from %s portal %d MB "LPX64" offset %d len %d\n",
316                op_mask == LNET_MD_OP_PUT ? "PUT" : "GET",
317                libcfs_id2str(src), index, match_bits, roffset, rlength);
318  out:
319         LNET_UNLOCK();
320         return rc;
321 }
322
323 void
324 lnet_ptl_detach_md(lnet_me_t *me, lnet_libmd_t *md)
325 {
326         LASSERT(me->me_md == md && md->md_me == me);
327
328         me->me_md = NULL;
329         md->md_me = NULL;
330 }
331
332 /* called with LNET_LOCK held */
333 void
334 lnet_ptl_attach_md(lnet_me_t *me, lnet_libmd_t *md,
335                    cfs_list_t *matches, cfs_list_t *drops)
336 {
337         struct lnet_portal      *ptl = the_lnet.ln_portals[me->me_portal];
338         lnet_msg_t              *tmp;
339         lnet_msg_t              *msg;
340
341         LASSERT(md->md_refcount == 0); /* a brand new MD */
342
343         me->me_md = md;
344         md->md_me = me;
345
346         cfs_list_for_each_entry_safe(msg, tmp, &ptl->ptl_msgq, msg_list) {
347                 int               rc;
348                 int               index;
349                 lnet_hdr_t       *hdr;
350                 lnet_process_id_t src;
351
352                 LASSERT(msg->msg_rx_delayed);
353
354                 hdr   = &msg->msg_hdr;
355                 index = hdr->msg.put.ptl_index;
356
357                 src.nid = hdr->src_nid;
358                 src.pid = hdr->src_pid;
359
360                 rc = lnet_try_match_md(index, LNET_MD_OP_PUT, src,
361                                        hdr->payload_length,
362                                        hdr->msg.put.offset,
363                                        hdr->msg.put.match_bits, md, msg);
364
365                 if (rc == LNET_MATCHMD_NONE)
366                         continue;
367
368                 /* Hurrah! This _is_ a match */
369                 cfs_list_del(&msg->msg_list);
370
371                 if (rc == LNET_MATCHMD_OK) {
372                         cfs_list_add_tail(&msg->msg_list, matches);
373
374                         CDEBUG(D_NET, "Resuming delayed PUT from %s portal %d "
375                                "match "LPU64" offset %d length %d.\n",
376                                libcfs_id2str(src),
377                                hdr->msg.put.ptl_index,
378                                hdr->msg.put.match_bits,
379                                hdr->msg.put.offset,
380                                hdr->payload_length);
381                 } else {
382                         LASSERT(rc == LNET_MATCHMD_DROP);
383
384                         cfs_list_add_tail(&msg->msg_list, drops);
385                 }
386
387                 if (lnet_md_exhausted(md))
388                         break;
389         }
390 }
391
392 void
393 lnet_ptl_cleanup(struct lnet_portal *ptl)
394 {
395         struct lnet_match_table *mtable;
396
397         LASSERT(cfs_list_empty(&ptl->ptl_msgq));
398
399         if (ptl->ptl_mtable == NULL) /* uninitialized portal */
400                 return;
401
402         do { /* iterate over match-tables when we have percpt match-table */
403                 cfs_list_t      *mhash;
404                 lnet_me_t       *me;
405                 int             j;
406
407                 mtable = ptl->ptl_mtable;
408
409                 if (mtable->mt_mhash == NULL) /* uninitialized match-table */
410                         continue;
411
412                 mhash = mtable->mt_mhash;
413                 /* cleanup ME */
414                 while (!cfs_list_empty(&mtable->mt_mlist)) {
415                         me = cfs_list_entry(mtable->mt_mlist.next,
416                                             lnet_me_t, me_list);
417                         CERROR("Active wildcard ME %p on exit\n", me);
418                         cfs_list_del(&me->me_list);
419                         lnet_me_free(me);
420                 }
421
422                 for (j = 0; j < LNET_MT_HASH_SIZE; j++) {
423                         while (!cfs_list_empty(&mhash[j])) {
424                                 me = cfs_list_entry(mhash[j].next,
425                                                     lnet_me_t, me_list);
426                                 CERROR("Active unique ME %p on exit\n", me);
427                                 cfs_list_del(&me->me_list);
428                                 lnet_me_free(me);
429                         }
430                 }
431
432                 LIBCFS_FREE(mhash, sizeof(*mhash) * LNET_MT_HASH_SIZE);
433         } while (0);
434
435         LIBCFS_FREE(ptl->ptl_mtable, sizeof(*mtable));
436         ptl->ptl_mtable = NULL;
437 }
438
439 int
440 lnet_ptl_setup(struct lnet_portal *ptl, int index)
441 {
442         struct lnet_match_table *mtable;
443         cfs_list_t              *mhash;
444         int                     j;
445
446         ptl->ptl_index = index;
447         CFS_INIT_LIST_HEAD(&ptl->ptl_msgq);
448
449         LIBCFS_ALLOC(mtable, sizeof(*mtable));
450         if (mtable == NULL) {
451                 CERROR("Failed to create match table for portal %d\n", index);
452                 return -ENOMEM;
453         }
454
455         ptl->ptl_mtable = mtable;
456         do { /* iterate over match-tables when we have percpt match-table */
457                 LIBCFS_ALLOC(mhash, sizeof(*mhash) * LNET_MT_HASH_SIZE);
458                 if (mhash == NULL) {
459                         CERROR("Failed to create match hash for portal %d\n",
460                                index);
461                         goto failed;
462                 }
463
464                 mtable->mt_mhash = mhash;
465                 for (j = 0; j < LNET_MT_HASH_SIZE; j++)
466                         CFS_INIT_LIST_HEAD(&mhash[j]);
467
468                 CFS_INIT_LIST_HEAD(&mtable->mt_mlist);
469                 mtable->mt_portal = index;
470         } while (0);
471
472         return 0;
473  failed:
474         lnet_ptl_cleanup(ptl);
475         return -ENOMEM;
476 }
477
478 void
479 lnet_portals_destroy(void)
480 {
481         int     i;
482
483         if (the_lnet.ln_portals == NULL)
484                 return;
485
486         for (i = 0; i < the_lnet.ln_nportals; i++)
487                 lnet_ptl_cleanup(the_lnet.ln_portals[i]);
488
489         cfs_array_free(the_lnet.ln_portals);
490         the_lnet.ln_portals = NULL;
491 }
492
493 int
494 lnet_portals_create(void)
495 {
496         int     size;
497         int     i;
498
499         size = sizeof(struct lnet_portal);
500
501         the_lnet.ln_nportals = MAX_PORTALS;
502         the_lnet.ln_portals = cfs_array_alloc(the_lnet.ln_nportals, size);
503         if (the_lnet.ln_portals == NULL) {
504                 CERROR("Failed to allocate portals table\n");
505                 return -ENOMEM;
506         }
507
508         for (i = 0; i < the_lnet.ln_nportals; i++) {
509                 if (lnet_ptl_setup(the_lnet.ln_portals[i], i)) {
510                         lnet_portals_destroy();
511                         return -ENOMEM;
512                 }
513         }
514
515         return 0;
516 }
517
518 /**
519  * Turn on the lazy portal attribute. Use with caution!
520  *
521  * This portal attribute only affects incoming PUT requests to the portal,
522  * and is off by default. By default, if there's no matching MD for an
523  * incoming PUT request, it is simply dropped. With the lazy attribute on,
524  * such requests are queued indefinitely until either a matching MD is
525  * posted to the portal or the lazy attribute is turned off.
526  *
527  * It would prevent dropped requests, however it should be regarded as the
528  * last line of defense - i.e. users must keep a close watch on active
529  * buffers on a lazy portal and once it becomes too low post more buffers as
530  * soon as possible. This is because delayed requests usually have detrimental
531  * effects on underlying network connections. A few delayed requests often
532  * suffice to bring an underlying connection to a complete halt, due to flow
533  * control mechanisms.
534  *
535  * There's also a DOS attack risk. If users don't post match-all MDs on a
536  * lazy portal, a malicious peer can easily stop a service by sending some
537  * PUT requests with match bits that won't match any MD. A routed server is
538  * especially vulnerable since the connections to its neighbor routers are
539  * shared among all clients.
540  *
541  * \param portal Index of the portal to enable the lazy attribute on.
542  *
543  * \retval 0       On success.
544  * \retval -EINVAL If \a portal is not a valid index.
545  */
546 int
547 LNetSetLazyPortal(int portal)
548 {
549         struct lnet_portal *ptl;
550
551         if (portal < 0 || portal >= the_lnet.ln_nportals)
552                 return -EINVAL;
553
554         CDEBUG(D_NET, "Setting portal %d lazy\n", portal);
555         ptl = the_lnet.ln_portals[portal];
556
557         LNET_LOCK();
558         lnet_ptl_setopt(ptl, LNET_PTL_LAZY);
559         LNET_UNLOCK();
560
561         return 0;
562 }
563
564 /**
565  * Turn off the lazy portal attribute. Delayed requests on the portal,
566  * if any, will be all dropped when this function returns.
567  *
568  * \param portal Index of the portal to disable the lazy attribute on.
569  *
570  * \retval 0       On success.
571  * \retval -EINVAL If \a portal is not a valid index.
572  */
573 int
574 LNetClearLazyPortal(int portal)
575 {
576         struct lnet_portal      *ptl;
577         CFS_LIST_HEAD           (zombies);
578
579         if (portal < 0 || portal >= the_lnet.ln_nportals)
580                 return -EINVAL;
581
582         ptl = the_lnet.ln_portals[portal];
583
584         LNET_LOCK();
585
586         if (!lnet_ptl_is_lazy(ptl)) {
587                 LNET_UNLOCK();
588                 return 0;
589         }
590
591         if (the_lnet.ln_shutdown)
592                 CWARN("Active lazy portal %d on exit\n", portal);
593         else
594                 CDEBUG(D_NET, "clearing portal %d lazy\n", portal);
595
596         /* grab all the blocked messages atomically */
597         cfs_list_splice_init(&ptl->ptl_msgq, &zombies);
598
599         lnet_ptl_unsetopt(ptl, LNET_PTL_LAZY);
600
601         LNET_UNLOCK();
602
603         lnet_drop_delayed_msg_list(&zombies, "Clearing lazy portal attr");
604
605         return 0;
606 }