~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/nfs/blocklayout/extent_tree.c

Version: ~ [ linux-5.15-rc1 ] ~ [ linux-5.14.5 ] ~ [ linux-5.13.18 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.66 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.147 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.206 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.246 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.282 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.283 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * Copyright (c) 2014-2016 Christoph Hellwig.
  4  */
  5 
  6 #include <linux/vmalloc.h>
  7 
  8 #include "blocklayout.h"
  9 
 10 #define NFSDBG_FACILITY         NFSDBG_PNFS_LD
 11 
 12 static inline struct pnfs_block_extent *
 13 ext_node(struct rb_node *node)
 14 {
 15         return rb_entry(node, struct pnfs_block_extent, be_node);
 16 }
 17 
 18 static struct pnfs_block_extent *
 19 ext_tree_first(struct rb_root *root)
 20 {
 21         struct rb_node *node = rb_first(root);
 22         return node ? ext_node(node) : NULL;
 23 }
 24 
 25 static struct pnfs_block_extent *
 26 ext_tree_prev(struct pnfs_block_extent *be)
 27 {
 28         struct rb_node *node = rb_prev(&be->be_node);
 29         return node ? ext_node(node) : NULL;
 30 }
 31 
 32 static struct pnfs_block_extent *
 33 ext_tree_next(struct pnfs_block_extent *be)
 34 {
 35         struct rb_node *node = rb_next(&be->be_node);
 36         return node ? ext_node(node) : NULL;
 37 }
 38 
 39 static inline sector_t
 40 ext_f_end(struct pnfs_block_extent *be)
 41 {
 42         return be->be_f_offset + be->be_length;
 43 }
 44 
 45 static struct pnfs_block_extent *
 46 __ext_tree_search(struct rb_root *root, sector_t start)
 47 {
 48         struct rb_node *node = root->rb_node;
 49         struct pnfs_block_extent *be = NULL;
 50 
 51         while (node) {
 52                 be = ext_node(node);
 53                 if (start < be->be_f_offset)
 54                         node = node->rb_left;
 55                 else if (start >= ext_f_end(be))
 56                         node = node->rb_right;
 57                 else
 58                         return be;
 59         }
 60 
 61         if (be) {
 62                 if (start < be->be_f_offset)
 63                         return be;
 64 
 65                 if (start >= ext_f_end(be))
 66                         return ext_tree_next(be);
 67         }
 68 
 69         return NULL;
 70 }
 71 
 72 static bool
 73 ext_can_merge(struct pnfs_block_extent *be1, struct pnfs_block_extent *be2)
 74 {
 75         if (be1->be_state != be2->be_state)
 76                 return false;
 77         if (be1->be_device != be2->be_device)
 78                 return false;
 79 
 80         if (be1->be_f_offset + be1->be_length != be2->be_f_offset)
 81                 return false;
 82 
 83         if (be1->be_state != PNFS_BLOCK_NONE_DATA &&
 84             (be1->be_v_offset + be1->be_length != be2->be_v_offset))
 85                 return false;
 86 
 87         if (be1->be_state == PNFS_BLOCK_INVALID_DATA &&
 88             be1->be_tag != be2->be_tag)
 89                 return false;
 90 
 91         return true;
 92 }
 93 
 94 static struct pnfs_block_extent *
 95 ext_try_to_merge_left(struct rb_root *root, struct pnfs_block_extent *be)
 96 {
 97         struct pnfs_block_extent *left = ext_tree_prev(be);
 98 
 99         if (left && ext_can_merge(left, be)) {
100                 left->be_length += be->be_length;
101                 rb_erase(&be->be_node, root);
102                 nfs4_put_deviceid_node(be->be_device);
103                 kfree(be);
104                 return left;
105         }
106 
107         return be;
108 }
109 
110 static struct pnfs_block_extent *
111 ext_try_to_merge_right(struct rb_root *root, struct pnfs_block_extent *be)
112 {
113         struct pnfs_block_extent *right = ext_tree_next(be);
114 
115         if (right && ext_can_merge(be, right)) {
116                 be->be_length += right->be_length;
117                 rb_erase(&right->be_node, root);
118                 nfs4_put_deviceid_node(right->be_device);
119                 kfree(right);
120         }
121 
122         return be;
123 }
124 
125 static void __ext_put_deviceids(struct list_head *head)
126 {
127         struct pnfs_block_extent *be, *tmp;
128 
129         list_for_each_entry_safe(be, tmp, head, be_list) {
130                 nfs4_put_deviceid_node(be->be_device);
131                 kfree(be);
132         }
133 }
134 
135 static void
136 __ext_tree_insert(struct rb_root *root,
137                 struct pnfs_block_extent *new, bool merge_ok)
138 {
139         struct rb_node **p = &root->rb_node, *parent = NULL;
140         struct pnfs_block_extent *be;
141 
142         while (*p) {
143                 parent = *p;
144                 be = ext_node(parent);
145 
146                 if (new->be_f_offset < be->be_f_offset) {
147                         if (merge_ok && ext_can_merge(new, be)) {
148                                 be->be_f_offset = new->be_f_offset;
149                                 if (be->be_state != PNFS_BLOCK_NONE_DATA)
150                                         be->be_v_offset = new->be_v_offset;
151                                 be->be_length += new->be_length;
152                                 be = ext_try_to_merge_left(root, be);
153                                 goto free_new;
154                         }
155                         p = &(*p)->rb_left;
156                 } else if (new->be_f_offset >= ext_f_end(be)) {
157                         if (merge_ok && ext_can_merge(be, new)) {
158                                 be->be_length += new->be_length;
159                                 be = ext_try_to_merge_right(root, be);
160                                 goto free_new;
161                         }
162                         p = &(*p)->rb_right;
163                 } else {
164                         BUG();
165                 }
166         }
167 
168         rb_link_node(&new->be_node, parent, p);
169         rb_insert_color(&new->be_node, root);
170         return;
171 free_new:
172         nfs4_put_deviceid_node(new->be_device);
173         kfree(new);
174 }
175 
176 static int
177 __ext_tree_remove(struct rb_root *root,
178                 sector_t start, sector_t end, struct list_head *tmp)
179 {
180         struct pnfs_block_extent *be;
181         sector_t len1 = 0, len2 = 0;
182         sector_t orig_v_offset;
183         sector_t orig_len;
184 
185         be = __ext_tree_search(root, start);
186         if (!be)
187                 return 0;
188         if (be->be_f_offset >= end)
189                 return 0;
190 
191         orig_v_offset = be->be_v_offset;
192         orig_len = be->be_length;
193 
194         if (start > be->be_f_offset)
195                 len1 = start - be->be_f_offset;
196         if (ext_f_end(be) > end)
197                 len2 = ext_f_end(be) - end;
198 
199         if (len2 > 0) {
200                 if (len1 > 0) {
201                         struct pnfs_block_extent *new;
202 
203                         new = kzalloc(sizeof(*new), GFP_ATOMIC);
204                         if (!new)
205                                 return -ENOMEM;
206 
207                         be->be_length = len1;
208 
209                         new->be_f_offset = end;
210                         if (be->be_state != PNFS_BLOCK_NONE_DATA) {
211                                 new->be_v_offset =
212                                         orig_v_offset + orig_len - len2;
213                         }
214                         new->be_length = len2;
215                         new->be_state = be->be_state;
216                         new->be_tag = be->be_tag;
217                         new->be_device = nfs4_get_deviceid(be->be_device);
218 
219                         __ext_tree_insert(root, new, true);
220                 } else {
221                         be->be_f_offset = end;
222                         if (be->be_state != PNFS_BLOCK_NONE_DATA) {
223                                 be->be_v_offset =
224                                         orig_v_offset + orig_len - len2;
225                         }
226                         be->be_length = len2;
227                 }
228         } else {
229                 if (len1 > 0) {
230                         be->be_length = len1;
231                         be = ext_tree_next(be);
232                 }
233 
234                 while (be && ext_f_end(be) <= end) {
235                         struct pnfs_block_extent *next = ext_tree_next(be);
236 
237                         rb_erase(&be->be_node, root);
238                         list_add_tail(&be->be_list, tmp);
239                         be = next;
240                 }
241 
242                 if (be && be->be_f_offset < end) {
243                         len1 = ext_f_end(be) - end;
244                         be->be_f_offset = end;
245                         if (be->be_state != PNFS_BLOCK_NONE_DATA)
246                                 be->be_v_offset += be->be_length - len1;
247                         be->be_length = len1;
248                 }
249         }
250 
251         return 0;
252 }
253 
254 int
255 ext_tree_insert(struct pnfs_block_layout *bl, struct pnfs_block_extent *new)
256 {
257         struct pnfs_block_extent *be;
258         struct rb_root *root;
259         int err = 0;
260 
261         switch (new->be_state) {
262         case PNFS_BLOCK_READWRITE_DATA:
263         case PNFS_BLOCK_INVALID_DATA:
264                 root = &bl->bl_ext_rw;
265                 break;
266         case PNFS_BLOCK_READ_DATA:
267         case PNFS_BLOCK_NONE_DATA:
268                 root = &bl->bl_ext_ro;
269                 break;
270         default:
271                 dprintk("invalid extent type\n");
272                 return -EINVAL;
273         }
274 
275         spin_lock(&bl->bl_ext_lock);
276 retry:
277         be = __ext_tree_search(root, new->be_f_offset);
278         if (!be || be->be_f_offset >= ext_f_end(new)) {
279                 __ext_tree_insert(root, new, true);
280         } else if (new->be_f_offset >= be->be_f_offset) {
281                 if (ext_f_end(new) <= ext_f_end(be)) {
282                         nfs4_put_deviceid_node(new->be_device);
283                         kfree(new);
284                 } else {
285                         sector_t new_len = ext_f_end(new) - ext_f_end(be);
286                         sector_t diff = new->be_length - new_len;
287 
288                         new->be_f_offset += diff;
289                         new->be_v_offset += diff;
290                         new->be_length = new_len;
291                         goto retry;
292                 }
293         } else if (ext_f_end(new) <= ext_f_end(be)) {
294                 new->be_length = be->be_f_offset - new->be_f_offset;
295                 __ext_tree_insert(root, new, true);
296         } else {
297                 struct pnfs_block_extent *split;
298                 sector_t new_len = ext_f_end(new) - ext_f_end(be);
299                 sector_t diff = new->be_length - new_len;
300 
301                 split = kmemdup(new, sizeof(*new), GFP_ATOMIC);
302                 if (!split) {
303                         err = -EINVAL;
304                         goto out;
305                 }
306 
307                 split->be_length = be->be_f_offset - split->be_f_offset;
308                 split->be_device = nfs4_get_deviceid(new->be_device);
309                 __ext_tree_insert(root, split, true);
310 
311                 new->be_f_offset += diff;
312                 new->be_v_offset += diff;
313                 new->be_length = new_len;
314                 goto retry;
315         }
316 out:
317         spin_unlock(&bl->bl_ext_lock);
318         return err;
319 }
320 
321 static bool
322 __ext_tree_lookup(struct rb_root *root, sector_t isect,
323                 struct pnfs_block_extent *ret)
324 {
325         struct rb_node *node;
326         struct pnfs_block_extent *be;
327 
328         node = root->rb_node;
329         while (node) {
330                 be = ext_node(node);
331                 if (isect < be->be_f_offset)
332                         node = node->rb_left;
333                 else if (isect >= ext_f_end(be))
334                         node = node->rb_right;
335                 else {
336                         *ret = *be;
337                         return true;
338                 }
339         }
340 
341         return false;
342 }
343 
344 bool
345 ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect,
346             struct pnfs_block_extent *ret, bool rw)
347 {
348         bool found = false;
349 
350         spin_lock(&bl->bl_ext_lock);
351         if (!rw)
352                 found = __ext_tree_lookup(&bl->bl_ext_ro, isect, ret);
353         if (!found)
354                 found = __ext_tree_lookup(&bl->bl_ext_rw, isect, ret);
355         spin_unlock(&bl->bl_ext_lock);
356 
357         return found;
358 }
359 
360 int ext_tree_remove(struct pnfs_block_layout *bl, bool rw,
361                 sector_t start, sector_t end)
362 {
363         int err, err2;
364         LIST_HEAD(tmp);
365 
366         spin_lock(&bl->bl_ext_lock);
367         err = __ext_tree_remove(&bl->bl_ext_ro, start, end, &tmp);
368         if (rw) {
369                 err2 = __ext_tree_remove(&bl->bl_ext_rw, start, end, &tmp);
370                 if (!err)
371                         err = err2;
372         }
373         spin_unlock(&bl->bl_ext_lock);
374 
375         __ext_put_deviceids(&tmp);
376         return err;
377 }
378 
379 static int
380 ext_tree_split(struct rb_root *root, struct pnfs_block_extent *be,
381                 sector_t split)
382 {
383         struct pnfs_block_extent *new;
384         sector_t orig_len = be->be_length;
385 
386         new = kzalloc(sizeof(*new), GFP_ATOMIC);
387         if (!new)
388                 return -ENOMEM;
389 
390         be->be_length = split - be->be_f_offset;
391 
392         new->be_f_offset = split;
393         if (be->be_state != PNFS_BLOCK_NONE_DATA)
394                 new->be_v_offset = be->be_v_offset + be->be_length;
395         new->be_length = orig_len - be->be_length;
396         new->be_state = be->be_state;
397         new->be_tag = be->be_tag;
398         new->be_device = nfs4_get_deviceid(be->be_device);
399 
400         __ext_tree_insert(root, new, false);
401         return 0;
402 }
403 
404 int
405 ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
406                 sector_t len, u64 lwb)
407 {
408         struct rb_root *root = &bl->bl_ext_rw;
409         sector_t end = start + len;
410         struct pnfs_block_extent *be;
411         int err = 0;
412         LIST_HEAD(tmp);
413 
414         spin_lock(&bl->bl_ext_lock);
415         /*
416          * First remove all COW extents or holes from written to range.
417          */
418         err = __ext_tree_remove(&bl->bl_ext_ro, start, end, &tmp);
419         if (err)
420                 goto out;
421 
422         /*
423          * Then mark all invalid extents in the range as written to.
424          */
425         for (be = __ext_tree_search(root, start); be; be = ext_tree_next(be)) {
426                 if (be->be_f_offset >= end)
427                         break;
428 
429                 if (be->be_state != PNFS_BLOCK_INVALID_DATA || be->be_tag)
430                         continue;
431 
432                 if (be->be_f_offset < start) {
433                         struct pnfs_block_extent *left = ext_tree_prev(be);
434 
435                         if (left && ext_can_merge(left, be)) {
436                                 sector_t diff = start - be->be_f_offset;
437 
438                                 left->be_length += diff;
439 
440                                 be->be_f_offset += diff;
441                                 be->be_v_offset += diff;
442                                 be->be_length -= diff;
443                         } else {
444                                 err = ext_tree_split(root, be, start);
445                                 if (err)
446                                         goto out;
447                         }
448                 }
449 
450                 if (ext_f_end(be) > end) {
451                         struct pnfs_block_extent *right = ext_tree_next(be);
452 
453                         if (right && ext_can_merge(be, right)) {
454                                 sector_t diff = end - be->be_f_offset;
455 
456                                 be->be_length -= diff;
457 
458                                 right->be_f_offset -= diff;
459                                 right->be_v_offset -= diff;
460                                 right->be_length += diff;
461                         } else {
462                                 err = ext_tree_split(root, be, end);
463                                 if (err)
464                                         goto out;
465                         }
466                 }
467 
468                 if (be->be_f_offset >= start && ext_f_end(be) <= end) {
469                         be->be_tag = EXTENT_WRITTEN;
470                         be = ext_try_to_merge_left(root, be);
471                         be = ext_try_to_merge_right(root, be);
472                 }
473         }
474 out:
475         if (bl->bl_lwb < lwb)
476                 bl->bl_lwb = lwb;
477         spin_unlock(&bl->bl_ext_lock);
478 
479         __ext_put_deviceids(&tmp);
480         return err;
481 }
482 
483 static size_t ext_tree_layoutupdate_size(struct pnfs_block_layout *bl, size_t count)
484 {
485         if (bl->bl_scsi_layout)
486                 return sizeof(__be32) + PNFS_SCSI_RANGE_SIZE * count;
487         else
488                 return sizeof(__be32) + PNFS_BLOCK_EXTENT_SIZE * count;
489 }
490 
491 static void ext_tree_free_commitdata(struct nfs4_layoutcommit_args *arg,
492                 size_t buffer_size)
493 {
494         if (arg->layoutupdate_pages != &arg->layoutupdate_page) {
495                 int nr_pages = DIV_ROUND_UP(buffer_size, PAGE_SIZE), i;
496 
497                 for (i = 0; i < nr_pages; i++)
498                         put_page(arg->layoutupdate_pages[i]);
499                 vfree(arg->start_p);
500                 kfree(arg->layoutupdate_pages);
501         } else {
502                 put_page(arg->layoutupdate_page);
503         }
504 }
505 
506 static __be32 *encode_block_extent(struct pnfs_block_extent *be, __be32 *p)
507 {
508         p = xdr_encode_opaque_fixed(p, be->be_device->deviceid.data,
509                         NFS4_DEVICEID4_SIZE);
510         p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT);
511         p = xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT);
512         p = xdr_encode_hyper(p, 0LL);
513         *p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA);
514         return p;
515 }
516 
517 static __be32 *encode_scsi_range(struct pnfs_block_extent *be, __be32 *p)
518 {
519         p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT);
520         return xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT);
521 }
522 
523 static int ext_tree_encode_commit(struct pnfs_block_layout *bl, __be32 *p,
524                 size_t buffer_size, size_t *count, __u64 *lastbyte)
525 {
526         struct pnfs_block_extent *be;
527         int ret = 0;
528 
529         spin_lock(&bl->bl_ext_lock);
530         for (be = ext_tree_first(&bl->bl_ext_rw); be; be = ext_tree_next(be)) {
531                 if (be->be_state != PNFS_BLOCK_INVALID_DATA ||
532                     be->be_tag != EXTENT_WRITTEN)
533                         continue;
534 
535                 (*count)++;
536                 if (ext_tree_layoutupdate_size(bl, *count) > buffer_size) {
537                         /* keep counting.. */
538                         ret = -ENOSPC;
539                         continue;
540                 }
541 
542                 if (bl->bl_scsi_layout)
543                         p = encode_scsi_range(be, p);
544                 else
545                         p = encode_block_extent(be, p);
546                 be->be_tag = EXTENT_COMMITTING;
547         }
548         *lastbyte = bl->bl_lwb - 1;
549         bl->bl_lwb = 0;
550         spin_unlock(&bl->bl_ext_lock);
551 
552         return ret;
553 }
554 
555 int
556 ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg)
557 {
558         struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout);
559         size_t count = 0, buffer_size = PAGE_SIZE;
560         __be32 *start_p;
561         int ret;
562 
563         dprintk("%s enter\n", __func__);
564 
565         arg->layoutupdate_page = alloc_page(GFP_NOFS);
566         if (!arg->layoutupdate_page)
567                 return -ENOMEM;
568         start_p = page_address(arg->layoutupdate_page);
569         arg->layoutupdate_pages = &arg->layoutupdate_page;
570 
571 retry:
572         ret = ext_tree_encode_commit(bl, start_p + 1, buffer_size, &count, &arg->lastbytewritten);
573         if (unlikely(ret)) {
574                 ext_tree_free_commitdata(arg, buffer_size);
575 
576                 buffer_size = ext_tree_layoutupdate_size(bl, count);
577                 count = 0;
578 
579                 arg->layoutupdate_pages =
580                         kcalloc(DIV_ROUND_UP(buffer_size, PAGE_SIZE),
581                                 sizeof(struct page *), GFP_NOFS);
582                 if (!arg->layoutupdate_pages)
583                         return -ENOMEM;
584 
585                 start_p = __vmalloc(buffer_size, GFP_NOFS);
586                 if (!start_p) {
587                         kfree(arg->layoutupdate_pages);
588                         return -ENOMEM;
589                 }
590 
591                 goto retry;
592         }
593 
594         *start_p = cpu_to_be32(count);
595         arg->layoutupdate_len = ext_tree_layoutupdate_size(bl, count);
596 
597         if (unlikely(arg->layoutupdate_pages != &arg->layoutupdate_page)) {
598                 void *p = start_p, *end = p + arg->layoutupdate_len;
599                 struct page *page = NULL;
600                 int i = 0;
601 
602                 arg->start_p = start_p;
603                 for ( ; p < end; p += PAGE_SIZE) {
604                         page = vmalloc_to_page(p);
605                         arg->layoutupdate_pages[i++] = page;
606                         get_page(page);
607                 }
608         }
609 
610         dprintk("%s found %zu ranges\n", __func__, count);
611         return 0;
612 }
613 
614 void
615 ext_tree_mark_committed(struct nfs4_layoutcommit_args *arg, int status)
616 {
617         struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout);
618         struct rb_root *root = &bl->bl_ext_rw;
619         struct pnfs_block_extent *be;
620 
621         dprintk("%s status %d\n", __func__, status);
622 
623         ext_tree_free_commitdata(arg, arg->layoutupdate_len);
624 
625         spin_lock(&bl->bl_ext_lock);
626         for (be = ext_tree_first(root); be; be = ext_tree_next(be)) {
627                 if (be->be_state != PNFS_BLOCK_INVALID_DATA ||
628                     be->be_tag != EXTENT_COMMITTING)
629                         continue;
630 
631                 if (status) {
632                         /*
633                          * Mark as written and try again.
634                          *
635                          * XXX: some real error handling here wouldn't hurt..
636                          */
637                         be->be_tag = EXTENT_WRITTEN;
638                 } else {
639                         be->be_state = PNFS_BLOCK_READWRITE_DATA;
640                         be->be_tag = 0;
641                 }
642 
643                 be = ext_try_to_merge_left(root, be);
644                 be = ext_try_to_merge_right(root, be);
645         }
646         spin_unlock(&bl->bl_ext_lock);
647 }
648 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp