Linux userland API discussions
 help / color / mirror / Atom feed
* Re: [RFCv2 1/5] mm/compaction: enable driver page migration
From: Konstantin Khlebnikov @ 2015-07-04 18:49 UTC (permalink / raw)
  To: Gioh Kim
  Cc: Jeff Layton, Bruce Fields, Vlastimil Babka, Joonsoo Kim, Al Viro,
	Michael S. Tsirkin, Minchan Kim, Rafael Aquini, linux-fsdevel,
	virtualization, Linux Kernel Mailing List, Linux API,
	linux-mm@kvack.org, Andrew Morton
In-Reply-To: <1435312710-15108-2-git-send-email-gioh.kim@lge.com>

On Fri, Jun 26, 2015 at 12:58 PM, Gioh Kim <gioh.kim@lge.com> wrote:
> Add framework to register callback functions and
> check pages migratable.
> There are some modes of page isolation so that isolate interface
> has an arguments of page address and isolation mode.
>
> Signed-off-by: Gioh Kim <gioh.kim@lge.com>
> ---
>  include/linux/compaction.h | 11 +++++++++++
>  include/linux/fs.h         |  2 ++
>  include/linux/page-flags.h | 19 +++++++++++++++++++
>  include/linux/pagemap.h    | 27 +++++++++++++++++++++++++++
>  4 files changed, 59 insertions(+)
>
> diff --git a/include/linux/compaction.h b/include/linux/compaction.h
> index aa8f61c..4e91a07 100644
> --- a/include/linux/compaction.h
> +++ b/include/linux/compaction.h
> @@ -1,6 +1,9 @@
>  #ifndef _LINUX_COMPACTION_H
>  #define _LINUX_COMPACTION_H
>
> +#include <linux/pagemap.h>
> +#include <linux/mm.h>
> +
>  /* Return values for compact_zone() and try_to_compact_pages() */
>  /* compaction didn't start as it was deferred due to past failures */
>  #define COMPACT_DEFERRED       0
> @@ -51,6 +54,10 @@ extern void compaction_defer_reset(struct zone *zone, int order,
>                                 bool alloc_success);
>  extern bool compaction_restarting(struct zone *zone, int order);
>
> +static inline bool driver_page_migratable(struct page *page)
> +{
> +       return PageMigratable(page) && mapping_migratable(page->mapping);
> +}
>  #else
>  static inline unsigned long try_to_compact_pages(gfp_t gfp_mask,
>                         unsigned int order, int alloc_flags,
> @@ -83,6 +90,10 @@ static inline bool compaction_deferred(struct zone *zone, int order)
>         return true;
>  }
>
> +static inline bool driver_page_migratable(struct page *page)
> +{
> +       return false
> +}
>  #endif /* CONFIG_COMPACTION */
>
>  #if defined(CONFIG_COMPACTION) && defined(CONFIG_SYSFS) && defined(CONFIG_NUMA)
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index a0653e5..2cc4b24 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -396,6 +396,8 @@ struct address_space_operations {
>          */
>         int (*migratepage) (struct address_space *,
>                         struct page *, struct page *, enum migrate_mode);
> +       bool (*isolatepage) (struct page *, isolate_mode_t);
> +       void (*putbackpage) (struct page *);
>         int (*launder_page) (struct page *);
>         int (*is_partially_uptodate) (struct page *, unsigned long,
>                                         unsigned long);
> diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> index 91b7f9b..c8a66de 100644
> --- a/include/linux/page-flags.h
> +++ b/include/linux/page-flags.h
> @@ -649,6 +649,25 @@ static inline void __ClearPageBalloon(struct page *page)
>         atomic_set(&page->_mapcount, -1);
>  }
>
> +#define PAGE_MIGRATABLE_MAPCOUNT_VALUE (-255)
> +
> +static inline int PageMigratable(struct page *page)
> +{
> +       return atomic_read(&page->_mapcount) == PAGE_MIGRATABLE_MAPCOUNT_VALUE;
> +}

I don't like the name. It's boring and overused.
Let's call it "mobile" PageMobile() That will be fun.

> +
> +static inline void __SetPageMigratable(struct page *page)
> +{
> +       VM_BUG_ON_PAGE(atomic_read(&page->_mapcount) != -1, page);
> +       atomic_set(&page->_mapcount, PAGE_MIGRATABLE_MAPCOUNT_VALUE);
> +}
> +
> +static inline void __ClearPageMigratable(struct page *page)
> +{
> +       VM_BUG_ON_PAGE(!PageMigratable(page), page);
> +       atomic_set(&page->_mapcount, -1);
> +}
> +
>  /*
>   * If network-based swap is enabled, sl*b must keep track of whether pages
>   * were allocated from pfmemalloc reserves.
> diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
> index 3e95fb6..a306798 100644
> --- a/include/linux/pagemap.h
> +++ b/include/linux/pagemap.h
> @@ -25,8 +25,35 @@ enum mapping_flags {
>         AS_MM_ALL_LOCKS = __GFP_BITS_SHIFT + 2, /* under mm_take_all_locks() */
>         AS_UNEVICTABLE  = __GFP_BITS_SHIFT + 3, /* e.g., ramdisk, SHM_LOCK */
>         AS_EXITING      = __GFP_BITS_SHIFT + 4, /* final truncate in progress */
> +       AS_MIGRATABLE   = __GFP_BITS_SHIFT + 5,

I think this is redudant. Mark at page should be enough.
That inode should just provide way for calling migration methods, that's all.

>  };
>
> +static inline void mapping_set_migratable(struct address_space *mapping)
> +{
> +       set_bit(AS_MIGRATABLE, &mapping->flags);
> +}
> +
> +static inline void mapping_clear_migratable(struct address_space *mapping)
> +{
> +       clear_bit(AS_MIGRATABLE, &mapping->flags);
> +}
> +
> +static inline int __mapping_ops(struct address_space *mapping)
> +{
> +       /* migrating page should define all following methods */
> +       return mapping->a_ops &&
> +               mapping->a_ops->migratepage &&
> +               mapping->a_ops->isolatepage &&
> +               mapping->a_ops->putbackpage;
> +}

This is sanity check or debug? You already have mark right at page.
You could check them with VM_BUG_ON in some place.

> +
> +static inline int mapping_migratable(struct address_space *mapping)
> +{
> +       if (mapping && __mapping_ops(mapping))
> +               return test_bit(AS_MIGRATABLE, &mapping->flags);
> +       return !!mapping;
> +}
> +
>  static inline void mapping_set_error(struct address_space *mapping, int error)
>  {
>         if (unlikely(error)) {
> --
> 1.9.1
>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [RFCv2 3/5] mm/balloon: apply driver page migratable into balloon
From: Konstantin Khlebnikov @ 2015-07-04 18:55 UTC (permalink / raw)
  To: Gioh Kim
  Cc: Rafael Aquini, Michael S. Tsirkin, Linux API,
	Linux Kernel Mailing List, Andrew Morton, virtualization,
	Bruce Fields, Minchan Kim, linux-mm@kvack.org, Al Viro,
	linux-fsdevel, Jeff Layton, Joonsoo Kim, Vlastimil Babka
In-Reply-To: <1435312710-15108-4-git-send-email-gioh.kim@lge.com>

On Fri, Jun 26, 2015 at 12:58 PM, Gioh Kim <gioh.kim@lge.com> wrote:
> Apply driver page migration into balloon driver.
>
> Signed-off-by: Gioh Kim <gioh.kim@lge.com>
> ---
>  drivers/virtio/virtio_balloon.c        |  3 +++
>  fs/proc/page.c                         |  3 +++
>  include/linux/balloon_compaction.h     | 33 +++++++++++++++++++++------------
>  include/uapi/linux/kernel-page-flags.h |  2 +-
>  mm/balloon_compaction.c                | 19 +++++++++++++++++--
>  5 files changed, 45 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
> index 82e80e0..c49b553 100644
> --- a/drivers/virtio/virtio_balloon.c
> +++ b/drivers/virtio/virtio_balloon.c
> @@ -505,6 +505,9 @@ static int virtballoon_probe(struct virtio_device *vdev)
>         balloon_devinfo_init(&vb->vb_dev_info);
>  #ifdef CONFIG_BALLOON_COMPACTION
>         vb->vb_dev_info.migratepage = virtballoon_migratepage;
> +       vb->vb_dev_info.inode = anon_inode_new();
> +       vb->vb_dev_info.inode->i_mapping->a_ops = &balloon_aops;
> +       mapping_set_migratable(vb->vb_dev_info.inode->i_mapping);
>  #endif
>
>         err = init_vqs(vb);
> diff --git a/fs/proc/page.c b/fs/proc/page.c
> index 7eee2d8..2dc3673 100644
> --- a/fs/proc/page.c
> +++ b/fs/proc/page.c
> @@ -146,6 +146,9 @@ u64 stable_page_flags(struct page *page)
>         if (PageBalloon(page))
>                 u |= 1 << KPF_BALLOON;
>
> +       if (PageMigratable(page))
> +               u |= 1 << KPF_MIGRATABLE;
> +
>         u |= kpf_copy_bit(k, KPF_LOCKED,        PG_locked);
>
>         u |= kpf_copy_bit(k, KPF_SLAB,          PG_slab);
> diff --git a/include/linux/balloon_compaction.h b/include/linux/balloon_compaction.h
> index 9b0a15d..e8a3670 100644
> --- a/include/linux/balloon_compaction.h
> +++ b/include/linux/balloon_compaction.h
> @@ -48,6 +48,7 @@
>  #include <linux/migrate.h>
>  #include <linux/gfp.h>
>  #include <linux/err.h>
> +#include <linux/fs.h>
>
>  /*
>   * Balloon device information descriptor.
> @@ -62,6 +63,7 @@ struct balloon_dev_info {
>         struct list_head pages;         /* Pages enqueued & handled to Host */
>         int (*migratepage)(struct balloon_dev_info *, struct page *newpage,
>                         struct page *page, enum migrate_mode mode);
> +       struct inode *inode;
>  };
>
>  extern struct page *balloon_page_enqueue(struct balloon_dev_info *b_dev_info);
> @@ -73,24 +75,28 @@ static inline void balloon_devinfo_init(struct balloon_dev_info *balloon)
>         spin_lock_init(&balloon->pages_lock);
>         INIT_LIST_HEAD(&balloon->pages);
>         balloon->migratepage = NULL;
> +       balloon->inode = NULL;
>  }
>
>  #ifdef CONFIG_BALLOON_COMPACTION
> -extern bool balloon_page_isolate(struct page *page);
> +extern const struct address_space_operations balloon_aops;
> +extern bool balloon_page_isolate(struct page *page,
> +                                isolate_mode_t mode);
>  extern void balloon_page_putback(struct page *page);
> -extern int balloon_page_migrate(struct page *newpage,
> +extern int balloon_page_migrate(struct address_space *mapping,
> +                               struct page *newpage,
>                                 struct page *page, enum migrate_mode mode);
>
>  /*
> - * __is_movable_balloon_page - helper to perform @page PageBalloon tests
> + * __is_movable_balloon_page - helper to perform @page PageMigratable tests
>   */
>  static inline bool __is_movable_balloon_page(struct page *page)
>  {
> -       return PageBalloon(page);
> +       return PageMigratable(page);
>  }
>
>  /*
> - * balloon_page_movable - test PageBalloon to identify balloon pages
> + * balloon_page_movable - test PageMigratable to identify balloon pages
>   *                       and PagePrivate to check that the page is not
>   *                       isolated and can be moved by compaction/migration.
>   *
> @@ -99,7 +105,7 @@ static inline bool __is_movable_balloon_page(struct page *page)
>   */
>  static inline bool balloon_page_movable(struct page *page)
>  {
> -       return PageBalloon(page) && PagePrivate(page);
> +       return PageMigratable(page) && PagePrivate(page);
>  }
>
>  /*
> @@ -108,7 +114,7 @@ static inline bool balloon_page_movable(struct page *page)
>   */
>  static inline bool isolated_balloon_page(struct page *page)
>  {
> -       return PageBalloon(page);
> +       return PageMigratable(page);
>  }
>
>  /*
> @@ -123,7 +129,8 @@ static inline bool isolated_balloon_page(struct page *page)
>  static inline void balloon_page_insert(struct balloon_dev_info *balloon,
>                                        struct page *page)
>  {
> -       __SetPageBalloon(page);
> +       page->mapping = balloon->inode->i_mapping;
> +       __SetPageMigratable(page);
>         SetPagePrivate(page);
>         set_page_private(page, (unsigned long)balloon);
>         list_add(&page->lru, &balloon->pages);
> @@ -139,7 +146,8 @@ static inline void balloon_page_insert(struct balloon_dev_info *balloon,
>   */
>  static inline void balloon_page_delete(struct page *page)
>  {
> -       __ClearPageBalloon(page);
> +       page->mapping = NULL;
> +       __ClearPageMigratable(page);

Please leave balloon pages marked as balloon.
Just check them in migration code like:
if (PageBalloon(page) || PageMobile(page)) ...


>         set_page_private(page, 0);
>         if (PagePrivate(page)) {
>                 ClearPagePrivate(page);
> @@ -166,13 +174,13 @@ static inline gfp_t balloon_mapping_gfp_mask(void)
>  static inline void balloon_page_insert(struct balloon_dev_info *balloon,
>                                        struct page *page)
>  {
> -       __SetPageBalloon(page);
> +       __SetPageMigratable(page);
>         list_add(&page->lru, &balloon->pages);
>  }
>
>  static inline void balloon_page_delete(struct page *page)
>  {
> -       __ClearPageBalloon(page);
> +       __ClearPageMigratable(page);
>         list_del(&page->lru);
>  }
>
> @@ -191,7 +199,8 @@ static inline bool isolated_balloon_page(struct page *page)
>         return false;
>  }
>
> -static inline bool balloon_page_isolate(struct page *page)
> +static inline bool balloon_page_isolate(struct page *page,
> +                                       isolate_mode_t mode)
>  {
>         return false;
>  }
> diff --git a/include/uapi/linux/kernel-page-flags.h b/include/uapi/linux/kernel-page-flags.h
> index a6c4962..65db3a6 100644
> --- a/include/uapi/linux/kernel-page-flags.h
> +++ b/include/uapi/linux/kernel-page-flags.h
> @@ -33,6 +33,6 @@
>  #define KPF_THP                        22
>  #define KPF_BALLOON            23
>  #define KPF_ZERO_PAGE          24
> -
> +#define KPF_MIGRATABLE         25
>
>  #endif /* _UAPILINUX_KERNEL_PAGE_FLAGS_H */
> diff --git a/mm/balloon_compaction.c b/mm/balloon_compaction.c
> index fcad832..df72846 100644
> --- a/mm/balloon_compaction.c
> +++ b/mm/balloon_compaction.c
> @@ -131,7 +131,7 @@ static inline void __putback_balloon_page(struct page *page)
>  }
>
>  /* __isolate_lru_page() counterpart for a ballooned page */
> -bool balloon_page_isolate(struct page *page)
> +bool balloon_page_isolate(struct page *page, isolate_mode_t mode)
>  {
>         /*
>          * Avoid burning cycles with pages that are yet under __free_pages(),
> @@ -175,6 +175,9 @@ bool balloon_page_isolate(struct page *page)
>  /* putback_lru_page() counterpart for a ballooned page */
>  void balloon_page_putback(struct page *page)
>  {
> +       if (!isolated_balloon_page(page))
> +               return;
> +
>         /*
>          * 'lock_page()' stabilizes the page and prevents races against
>          * concurrent isolation threads attempting to re-isolate it.
> @@ -193,12 +196,16 @@ void balloon_page_putback(struct page *page)
>  }
>
>  /* move_to_new_page() counterpart for a ballooned page */
> -int balloon_page_migrate(struct page *newpage,
> +int balloon_page_migrate(struct address_space *mapping,
> +                        struct page *newpage,
>                          struct page *page, enum migrate_mode mode)
>  {
>         struct balloon_dev_info *balloon = balloon_page_device(page);
>         int rc = -EAGAIN;
>
> +       if (!isolated_balloon_page(page))
> +               return rc;
> +
>         /*
>          * Block others from accessing the 'newpage' when we get around to
>          * establishing additional references. We should be the only one
> @@ -218,4 +225,12 @@ int balloon_page_migrate(struct page *newpage,
>         unlock_page(newpage);
>         return rc;
>  }
> +
> +/* define the balloon_mapping->a_ops callback to allow balloon page migration */
> +const struct address_space_operations balloon_aops = {
> +       .migratepage = balloon_page_migrate,
> +       .isolatepage = balloon_page_isolate,
> +       .putbackpage = balloon_page_putback,
> +};
> +EXPORT_SYMBOL_GPL(balloon_aops);
>  #endif /* CONFIG_BALLOON_COMPACTION */
> --
> 1.9.1
>

^ permalink raw reply

* Re: [RFCv2 4/5] mm/compaction: compaction calls generic migration
From: Konstantin Khlebnikov @ 2015-07-04 19:00 UTC (permalink / raw)
  To: Gioh Kim
  Cc: Jeff Layton, Bruce Fields, Vlastimil Babka, Joonsoo Kim, Al Viro,
	Michael S. Tsirkin, Minchan Kim, Rafael Aquini, linux-fsdevel,
	virtualization, Linux Kernel Mailing List, Linux API,
	linux-mm@kvack.org, Andrew Morton
In-Reply-To: <CALYGNiPSXP5f9hnYjmHJrg7GQE+fM0RuKQOSu7QpWO5EmbiGoQ@mail.gmail.com>

On Sat, Jul 4, 2015 at 9:13 PM, Konstantin Khlebnikov <koct9i@gmail.com> wrote:
> On Fri, Jun 26, 2015 at 12:58 PM, Gioh Kim <gioh.kim@lge.com> wrote:
>> Compaction calls interfaces of driver page migration
>> instead of calling balloon migration directly.
>>
>> Signed-off-by: Gioh Kim <gioh.kim@lge.com>
>> ---
>>  drivers/virtio/virtio_balloon.c |  1 +
>>  mm/compaction.c                 |  9 +++++----
>>  mm/migrate.c                    | 21 ++++++++++++---------
>>  3 files changed, 18 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
>> index c49b553..5e5cbea 100644
>> --- a/drivers/virtio/virtio_balloon.c
>> +++ b/drivers/virtio/virtio_balloon.c
>> @@ -30,6 +30,7 @@
>>  #include <linux/balloon_compaction.h>
>>  #include <linux/oom.h>
>>  #include <linux/wait.h>
>> +#include <linux/anon_inodes.h>
>>
>>  /*
>>   * Balloon device works in 4K page units.  So each page is pointed to by
>> diff --git a/mm/compaction.c b/mm/compaction.c
>> index 16e1b57..cc5ec81 100644
>> --- a/mm/compaction.c
>> +++ b/mm/compaction.c
>> @@ -14,7 +14,7 @@
>>  #include <linux/backing-dev.h>
>>  #include <linux/sysctl.h>
>>  #include <linux/sysfs.h>
>> -#include <linux/balloon_compaction.h>
>> +#include <linux/compaction.h>
>>  #include <linux/page-isolation.h>
>>  #include <linux/kasan.h>
>>  #include "internal.h"
>> @@ -714,12 +714,13 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
>>
>>                 /*
>>                  * Check may be lockless but that's ok as we recheck later.
>> -                * It's possible to migrate LRU pages and balloon pages
>> +                * It's possible to migrate LRU pages and driver pages
>>                  * Skip any other type of page
>>                  */
>>                 if (!PageLRU(page)) {
>> -                       if (unlikely(balloon_page_movable(page))) {
>> -                               if (balloon_page_isolate(page)) {
>> +                       if (unlikely(driver_page_migratable(page))) {
>> +                               if (page->mapping->a_ops->isolatepage(page,
>> +                                                               isolate_mode)) {
>
> Dereferencing page->mapping isn't safe here.
> Page could be "truncated" from mapping at any time.
>
> As you can see  balloon_page_isolate() calls get_page_unless_zero,
> trylock_page and only after that checks balloon_page_movable again.

Page must be getted and locked before calling aops method, somethin like this:

If (!PageLRU(page)) {
   if (PageBalloon(page) || PageMobile(page))
       if (get_page_unless_zero(page))
           if (try_lock(page))
              if (page->mapping && page->mapping->a_ops->isolatepage)
                  page->mapping->a_ops->isolate_page(page, ...)
....

>
> Existing code already does similar unsafe dereference in
> __isolate_lru_page(): page->mapping->a_ops->migratepage

>
>>                                         /* Successfully isolated */
>>                                         goto isolate_success;
>>                                 }
>> diff --git a/mm/migrate.c b/mm/migrate.c
>> index 236ee25..a0bc1e4 100644
>> --- a/mm/migrate.c
>> +++ b/mm/migrate.c
>> @@ -35,7 +35,7 @@
>>  #include <linux/hugetlb.h>
>>  #include <linux/hugetlb_cgroup.h>
>>  #include <linux/gfp.h>
>> -#include <linux/balloon_compaction.h>
>> +#include <linux/compaction.h>
>>  #include <linux/mmu_notifier.h>
>>
>>  #include <asm/tlbflush.h>
>> @@ -76,7 +76,7 @@ int migrate_prep_local(void)
>>   * from where they were once taken off for compaction/migration.
>>   *
>>   * This function shall be used whenever the isolated pageset has been
>> - * built from lru, balloon, hugetlbfs page. See isolate_migratepages_range()
>> + * built from lru, driver, hugetlbfs page. See isolate_migratepages_range()
>>   * and isolate_huge_page().
>>   */
>>  void putback_movable_pages(struct list_head *l)
>> @@ -92,8 +92,8 @@ void putback_movable_pages(struct list_head *l)
>>                 list_del(&page->lru);
>>                 dec_zone_page_state(page, NR_ISOLATED_ANON +
>>                                 page_is_file_cache(page));
>> -               if (unlikely(isolated_balloon_page(page)))
>> -                       balloon_page_putback(page);
>> +               if (unlikely(driver_page_migratable(page)))
>> +                       page->mapping->a_ops->putbackpage(page);
>>                 else
>>                         putback_lru_page(page);
>>         }
>> @@ -844,15 +844,18 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
>>                 }
>>         }
>>
>> -       if (unlikely(isolated_balloon_page(page))) {
>> +       if (unlikely(driver_page_migratable(page))) {
>>                 /*
>> -                * A ballooned page does not need any special attention from
>> +                * A driver page does not need any special attention from
>>                  * physical to virtual reverse mapping procedures.
>>                  * Skip any attempt to unmap PTEs or to remap swap cache,
>>                  * in order to avoid burning cycles at rmap level, and perform
>>                  * the page migration right away (proteced by page lock).
>>                  */
>> -               rc = balloon_page_migrate(newpage, page, mode);
>> +               rc = page->mapping->a_ops->migratepage(page->mapping,
>> +                                                      newpage,
>> +                                                      page,
>> +                                                      mode);
>>                 goto out_unlock;
>>         }
>>
>> @@ -962,8 +965,8 @@ out:
>>         if (rc != MIGRATEPAGE_SUCCESS && put_new_page) {
>>                 ClearPageSwapBacked(newpage);
>>                 put_new_page(newpage, private);
>> -       } else if (unlikely(__is_movable_balloon_page(newpage))) {
>> -               /* drop our reference, page already in the balloon */
>> +       } else if (unlikely(driver_page_migratable(newpage))) {
>> +               /* drop our reference */
>>                 put_page(newpage);
>>         } else
>>                 putback_lru_page(newpage);
>> --
>> 1.9.1
>>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [GIT PULL] User namespace related fixes for v4.2
From: Al Viro @ 2015-07-04 23:11 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Seth Forshee, Linux API, Linux Containers, Greg Kroah-Hartman,
	Andy Lutomirski, Kenton Varda, Tejun Heo, Eric W. Biederman,
	Richard Weinberger,
	<linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
	Michael Kerrisk-manpages, Ivan Delalande
In-Reply-To: <CA+55aFw-DK-xDC-3HYa=BMX8WNyQgT9O01tihrAS9+-7PPj_jA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Jul 03, 2015 at 03:10:40PM -0700, Linus Torvalds wrote:
> On Mon, Jun 29, 2015 at 2:13 PM, Eric W. Biederman
> <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org> wrote:
> >
> > Can you live with the patch below and committing to never supporting
> > executables on proc and sysfs?
> 
> Sure. I don't think executables make any sense what-so-ever in those
> filesystems. I think it's fine saying that /proc and /sys cannot have
> executables in them, and then use that flag to just ignore the
> relevant mount flags.
> 
> Al, comments?

	I can live with that, but I would prefer that to be a superblock
flag force-set in ->mount() (and preserved in ->remount_fs()) rather than
Yet Another FS Type Flag.  OTOH, it's not hard to change afterwards.

	Al, bloody annoyed by having spent hours debugging an odd corruption
in merge candidate, only to find that it correlated to temperature of the
host ;-/  Seem to be all gone after replacing CPU fan and cleaning the
mess under it...

^ permalink raw reply

* Re: [PATCH RESEND] virtio: Fix typecast of pointer in vring_init()
From: Michael S. Tsirkin @ 2015-07-05 10:58 UTC (permalink / raw)
  To: Thomas Huth
  Cc: virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1435821682-1809-1-git-send-email-thuth-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

On Thu, Jul 02, 2015 at 09:21:22AM +0200, Thomas Huth wrote:
> The virtio_ring.h header is used in userspace programs (ie. QEMU),
> too. Here we can not assume that sizeof(pointer) is the same as
> sizeof(long), e.g. when compiling for Windows, so the typecast in
> vring_init() should be done with (uintptr_t) instead of (unsigned long).
> 
> Signed-off-by: Thomas Huth <thuth-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

This seems to break some userspace too:

  INSTALL usr/include/linux/ (413 files)
  CHECK   usr/include/linux/ (413 files)
  HOSTCC  Documentation/accounting/getdelays
  HOSTCC  Documentation/connector/ucon
  HOSTCC  Documentation/mic/mpssd/mpssd.o
In file included from Documentation/mic/mpssd/mpssd.c:36:0:
./usr/include/linux/virtio_ring.h: In function ‘vring_init’:
./usr/include/linux/virtio_ring.h:146:24: error: ‘uintptr_t’ undeclared
(first use in this function)
  vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] +
sizeof(__virtio16)
                        ^
./usr/include/linux/virtio_ring.h:146:24: note: each undeclared
identifier is reported only once for each function it appears in
scripts/Makefile.host:108: recipe for target
'Documentation/mic/mpssd/mpssd.o' failed
make[3]: *** [Documentation/mic/mpssd/mpssd.o] Error 1
scripts/Makefile.build:403: recipe for target 'Documentation/mic/mpssd'
failed
make[2]: *** [Documentation/mic/mpssd] Error 2
scripts/Makefile.build:403: recipe for target 'Documentation/mic' failed
make[1]: *** [Documentation/mic] Error 2


E.g. fuse.h has this code:
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif

Maybe we need something similar.

> ---
>  include/uapi/linux/virtio_ring.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/include/uapi/linux/virtio_ring.h b/include/uapi/linux/virtio_ring.h
> index 915980a..8682551 100644
> --- a/include/uapi/linux/virtio_ring.h
> +++ b/include/uapi/linux/virtio_ring.h
> @@ -143,7 +143,7 @@ static inline void vring_init(struct vring *vr, unsigned int num, void *p,
>  	vr->num = num;
>  	vr->desc = p;
>  	vr->avail = p + num*sizeof(struct vring_desc);
> -	vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(__virtio16)
> +	vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(__virtio16)
>  		+ align-1) & ~(align - 1));
>  }
>  
> -- 
> 1.8.3.1

^ permalink raw reply

* Re: [PATCH RESEND] virtio: Fix typecast of pointer in vring_init()
From: Michael S. Tsirkin @ 2015-07-05 12:59 UTC (permalink / raw)
  To: Thomas Huth
  Cc: virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20150705124937-mutt-send-email-mst-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

On Sun, Jul 05, 2015 at 12:58:53PM +0200, Michael S. Tsirkin wrote:
> On Thu, Jul 02, 2015 at 09:21:22AM +0200, Thomas Huth wrote:
> > The virtio_ring.h header is used in userspace programs (ie. QEMU),
> > too. Here we can not assume that sizeof(pointer) is the same as
> > sizeof(long), e.g. when compiling for Windows, so the typecast in
> > vring_init() should be done with (uintptr_t) instead of (unsigned long).
> > 
> > Signed-off-by: Thomas Huth <thuth-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> 
> This seems to break some userspace too:
> 
>   INSTALL usr/include/linux/ (413 files)
>   CHECK   usr/include/linux/ (413 files)
>   HOSTCC  Documentation/accounting/getdelays
>   HOSTCC  Documentation/connector/ucon
>   HOSTCC  Documentation/mic/mpssd/mpssd.o
> In file included from Documentation/mic/mpssd/mpssd.c:36:0:
> ./usr/include/linux/virtio_ring.h: In function ‘vring_init’:
> ./usr/include/linux/virtio_ring.h:146:24: error: ‘uintptr_t’ undeclared
> (first use in this function)
>   vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] +
> sizeof(__virtio16)
>                         ^
> ./usr/include/linux/virtio_ring.h:146:24: note: each undeclared
> identifier is reported only once for each function it appears in
> scripts/Makefile.host:108: recipe for target
> 'Documentation/mic/mpssd/mpssd.o' failed
> make[3]: *** [Documentation/mic/mpssd/mpssd.o] Error 1
> scripts/Makefile.build:403: recipe for target 'Documentation/mic/mpssd'
> failed
> make[2]: *** [Documentation/mic/mpssd] Error 2
> scripts/Makefile.build:403: recipe for target 'Documentation/mic' failed
> make[1]: *** [Documentation/mic] Error 2
> 
> 
> E.g. fuse.h has this code:
> #ifdef __KERNEL__
> #include <linux/types.h>
> #else
> #include <stdint.h>
> #endif
> 
> Maybe we need something similar.

The following seems to help.  Does it help the windows build?

---
 include/uapi/linux/virtio_ring.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/uapi/linux/virtio_ring.h b/include/uapi/linux/virtio_ring.h
index 8682551..c072959 100644
--- a/include/uapi/linux/virtio_ring.h
+++ b/include/uapi/linux/virtio_ring.h
@@ -31,6 +31,9 @@
  * SUCH DAMAGE.
  *
  * Copyright Rusty Russell IBM Corporation 2007. */
+#ifndef __KERNEL__
+#include <stdint.h>
+#endif
 #include <linux/types.h>
 #include <linux/virtio_types.h>
 
-- 
MST

^ permalink raw reply related

* Re: [PATCH v2 3/3] console_codes.4: Add CSI sequence for cursor blink interval
From: Scot Doyle @ 2015-07-05 17:41 UTC (permalink / raw)
  To: Michael Kerrisk
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-man-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <alpine.DEB.2.11.1503261356490.2411@local>

On Thu, 26 Mar 2015, Scot Doyle wrote:
> Add a Console Private CSI sequence to specify the current console's
> cursor blink interval. The interval is specified as a number of
> milliseconds until the next cursor display state toggle, from 50 to
> 65535.
> 
> Signed-off-by: Scot Doyle <lkml14-enLWO88E2pdl57MIdRCFDg@public.gmane.org>
> ---
>  man4/console_codes.4 | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/man4/console_codes.4 b/man4/console_codes.4
> index 34f7535..7d05076 100644
> --- a/man4/console_codes.4
> +++ b/man4/console_codes.4
> @@ -377,6 +377,7 @@ ESC [ 15 ]      	T{
>  Bring the previous console to the front
>  (since Linux 2.6.0).
>  T}
> +ESC [ 16 ; \fIn\fP ]   	Set the cursor blink interval in milliseconds.
>  .TE
>  .SS Character sets
>  The kernel knows about 4 translations of bytes into console-screen
> -- 
> 2.1.0
> 

Hi Michael,

Will you apply now that Linus has pulled the rest?
(see bd63364caa8df38bad2b25b11b2a1b849475cce5)

Thank you,
Scot
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Odd hang on suspend and shutdown
From: Linus Torvalds @ 2015-07-05 23:20 UTC (permalink / raw)
  To: Tomas Winkler, Greg Kroah-Hartman; +Cc: Linux API, Samuel Ortiz

On my Sony VAIO Pro 11 laptop, commit c93b76b34b4d ("mei: bus: report
also uuid in module alias") seems to cause problems at suspend and
shutdown.

In particular, reverting just the oneliner to drivers/nfc/pn544/mei.c
seems to fix things for me. The bisection was a pain, because it took
me forever to realize that it was that one-liner that caused it: I had
initially undone that one line simply because it didn't compile with
it in place (complaints about MEI_NFC_UUID not being constant). So I
continued to bisect with the fix unintentionally in place.

So just removing the MEI_NFC_UUID entry from the pn544_mei_tbl[] array
initialization makes things work for me again.

The symptoms are just a hard hang at suspend or shutdown.

This is a pretty regular Intel-only laptop, running Fedora 22.

Any ideas?

                    Linus

^ permalink raw reply

* Re: Odd hang on suspend and shutdown
From: Linus Torvalds @ 2015-07-05 23:24 UTC (permalink / raw)
  To: Tomas Winkler, Greg Kroah-Hartman; +Cc: Linux API, Samuel Ortiz
In-Reply-To: <CA+55aFy1x6iwbdV8WfR+wawj_1+PxJ+P-Js=EVqD9ZsQetSNJA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Sun, Jul 5, 2015 at 4:20 PM, Linus Torvalds
<torvalds-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org> wrote:
>
> So just removing the MEI_NFC_UUID entry from the pn544_mei_tbl[] array
> initialization makes things work for me again.

Side note: the fact that the initial commit didn't even compile, and
that this seems to be an API change, _and_ that it breaks things for a
laptop of mine makes me suspect that the answer is "revert the crap as
an obvious regression, and let's make sure it never resurfaces".

But I'm willing to entertain sane alternatives. Not for very long,
though, because I hate having known breakage on one of my machines.

                     Linus

^ permalink raw reply

* [PATCH 0/24] kernel: add a netlink interface to get information about processes (v2)
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov

Currently we use the proc file system, where all information are
presented in text files, what is convenient for humans.  But if we need
to get information about processes from code (e.g. in C), the procfs
doesn't look so cool.

From code we would prefer to get information in binary format and to be
able to specify which information and for which tasks are required. Here
is a new interface with all these features, which is called task_diag.
In addition it's much faster than procfs.

task_diag is based on netlink sockets and looks like socket-diag, which
is used to get information about sockets.

A request is described by the task_diag_pid structure:

struct task_diag_pid {
       __u64   show_flags;	/* specify which information are required */
       __u64   dump_stratagy;   /* specify a group of processes */

       __u32   pid;
};

dump_stratagy specifies a group of processes:
/* per-process strategies */
TASK_DIAG_DUMP_CHILDREN	- all children
TASK_DIAG_DUMP_THREAD	- all threads
TASK_DIAG_DUMP_ONE	- one process
/* system wide strategies (the pid fiel is ignored) */
TASK_DIAG_DUMP_ALL	  - all processes
TASK_DIAG_DUMP_ALL_THREAD - all threads

show_flags specifies which information are required.
If we set the TASK_DIAG_SHOW_BASE flag, the response message will
contain the TASK_DIAG_BASE attribute which is described by the
task_diag_base structure.

struct task_diag_base {
	__u32	tgid;
	__u32	pid;
	__u32	ppid;
	__u32	tpid;
	__u32	sid;
	__u32	pgid;
	__u8	state;
	char	comm[TASK_DIAG_COMM_LEN];
};

In future, it can be extended by optional attributes. The request
describes which task properties are required and for which processes
they are required for.

A response can be divided into a few netlink packets if the NETLINK_DUMP
has been set in a request. Each task is described by a message. Each
message contains the TASK_DIAG_PID attribute and optional attributes
which have been requested (show_flags). A message can be divided into a
few parts if it doesn’t fit into a current netlink packet. In this case,
the first message in the next packet contains the same PID and
attributes which doesn’t  fit into the previous message.

The task diag is much faster than the proc file system. We don't need to
create a new file descriptor for each task. We need to send a request
and get a response. It allows to get information for a few tasks in one
request-response iteration.

As for security, task_diag always works as procfs with hidepid = 2 (highest
level of security).

I have compared performance of procfs and task-diag for the
"ps ax -o pid,ppid" command.

A test stand contains 30108 processes.
$ ps ax -o pid,ppid | wc -l
30108

$ time ps ax -o pid,ppid > /dev/null

real	0m0.836s
user	0m0.238s
sys	0m0.583s

Read /proc/PID/stat for each task
$ time ./task_proc_all > /dev/null

real	0m0.258s
user	0m0.019s
sys	0m0.232s

$ time ./task_diag_all > /dev/null

real	0m0.052s
user	0m0.013s
sys	0m0.036s

And here are statistics on syscalls which were called by each
command.

$ perf trace -s -o log -- ./task_proc_all > /dev/null

 Summary of events:

 task_proc_all (30781), 180785 events, 100.0%, 0.000 msec

   syscall            calls      min       avg       max      stddev
                               (msec)    (msec)    (msec)        (%)
   --------------- -------- --------- --------- ---------     ------
   read               30111     0.000     0.013     0.107      0.21%
   write                  1     0.008     0.008     0.008      0.00%
   open               30111     0.007     0.012     0.145      0.24%
   close              30112     0.004     0.011     0.110      0.20%
   fstat                  3     0.009     0.013     0.016     16.15%
   mmap                   8     0.011     0.020     0.027     11.24%
   mprotect               4     0.019     0.023     0.028      8.33%
   munmap                 1     0.026     0.026     0.026      0.00%
   brk                    8     0.007     0.015     0.024     11.94%
   ioctl                  1     0.007     0.007     0.007      0.00%
   access                 1     0.019     0.019     0.019      0.00%
   execve                 1     0.000     0.000     0.000      0.00%
   getdents              29     0.008     1.010     2.215      8.88%
   arch_prctl             1     0.016     0.016     0.016      0.00%
   openat                 1     0.021     0.021     0.021      0.00%


$ perf trace -s -o log -- ./task_diag_all > /dev/null
 Summary of events:

 task_diag_all (30762), 717 events, 98.9%, 0.000 msec

   syscall            calls      min       avg       max      stddev
                               (msec)    (msec)    (msec)        (%)
   --------------- -------- --------- --------- ---------     ------
   read                   2     0.000     0.008     0.016    100.00%
   write                197     0.008     0.019     0.041      3.00%
   open                   2     0.023     0.029     0.036     22.45%
   close                  3     0.010     0.012     0.014     11.34%
   fstat                  3     0.012     0.044     0.106     70.52%
   mmap                   8     0.014     0.031     0.054     18.88%
   mprotect               4     0.016     0.023     0.027     10.93%
   munmap                 1     0.022     0.022     0.022      0.00%
   brk                    1     0.040     0.040     0.040      0.00%
   ioctl                  1     0.011     0.011     0.011      0.00%
   access                 1     0.032     0.032     0.032      0.00%
   getpid                 1     0.012     0.012     0.012      0.00%
   socket                 1     0.032     0.032     0.032      0.00%
   sendto                 2     0.032     0.095     0.157     65.77%
   recvfrom             129     0.009     0.235     0.418      2.45%
   bind                   1     0.018     0.018     0.018      0.00%
   execve                 1     0.000     0.000     0.000      0.00%
   arch_prctl             1     0.012     0.012     0.012      0.00%

You can find the test program from this experiment in tools/test/selftest/taskdiag.

The idea of this functionality was suggested by Pavel Emelyanov
(xemul@), when he found that operations with /proc forms a significant
part of a checkpointing time.

Ten years ago there was attempt to add a netlink interface to access to /proc
information:
http://lwn.net/Articles/99600/

git repo: https://github.com/avagin/linux-task-diag

Changes from the first version:

David Ahern implemented all required functionality to use task_diag in
perf.

Bellow you can find his results how it affects performance.
> Using the fork test command:
>    10,000 processes; 10k proc with 5 threads = 50,000 tasks
>    reading /proc: 11.3 sec
>    task_diag:      2.2 sec
>
> @7,440 tasks, reading /proc is at 0.77 sec and task_diag at 0.096
>
> 128 instances of sepcjbb, 80,000+ tasks:
>     reading /proc: 32.1 sec
>     task_diag:      3.9 sec
>
> So overall much snappier startup times.

Many thanks to David Ahern for the help with improving task_diag.

Cc: Oleg Nesterov <oleg-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: Andrew Morton <akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org>
Cc: Cyrill Gorcunov <gorcunov-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
Cc: Pavel Emelyanov <xemul-bzQdu9zFT3WakBO8gow8eQ@public.gmane.org>
Cc: Roger Luethi <rl-7uj+XXdSDtwfv37vnLkPlQ@public.gmane.org>
Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Cc: Arnaldo Carvalho de Melo <acme-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: David Ahern <dsahern-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org>
Cc: Pavel Odintsov <pavel.odintsov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
--
2.1.0

^ permalink raw reply

* [PATCH 01/24] kernel: define taststats commands in the one place
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

Currently if we add a new TASKSTATS_ constant, we will chanage all
CGROUPSTATS_ contants and break backward compatibility.

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 include/uapi/linux/cgroupstats.h | 15 ---------------
 include/uapi/linux/taskstats.h   |  7 +++++++
 2 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/include/uapi/linux/cgroupstats.h b/include/uapi/linux/cgroupstats.h
index 3753c33..8095931 100644
--- a/include/uapi/linux/cgroupstats.h
+++ b/include/uapi/linux/cgroupstats.h
@@ -37,21 +37,6 @@ struct cgroupstats {
 	__u64	nr_io_wait;		/* Number of tasks waiting on IO */
 };
 
-/*
- * Commands sent from userspace
- * Not versioned. New commands should only be inserted at the enum's end
- * prior to __CGROUPSTATS_CMD_MAX
- */
-
-enum {
-	CGROUPSTATS_CMD_UNSPEC = __TASKSTATS_CMD_MAX,	/* Reserved */
-	CGROUPSTATS_CMD_GET,		/* user->kernel request/get-response */
-	CGROUPSTATS_CMD_NEW,		/* kernel->user event */
-	__CGROUPSTATS_CMD_MAX,
-};
-
-#define CGROUPSTATS_CMD_MAX (__CGROUPSTATS_CMD_MAX - 1)
-
 enum {
 	CGROUPSTATS_TYPE_UNSPEC = 0,	/* Reserved */
 	CGROUPSTATS_TYPE_CGROUP_STATS,	/* contains name + stats */
diff --git a/include/uapi/linux/taskstats.h b/include/uapi/linux/taskstats.h
index 2466e55..a1cc91b 100644
--- a/include/uapi/linux/taskstats.h
+++ b/include/uapi/linux/taskstats.h
@@ -176,9 +176,16 @@ enum {
 	TASKSTATS_CMD_UNSPEC = 0,	/* Reserved */
 	TASKSTATS_CMD_GET,		/* user->kernel request/get-response */
 	TASKSTATS_CMD_NEW,		/* kernel->user event */
+	__TASKSTATS_CMD_RESERVED,
+
+	CGROUPSTATS_CMD_GET,		/* user->kernel request/get-response */
+	CGROUPSTATS_CMD_NEW,		/* kernel->user event */
+
 	__TASKSTATS_CMD_MAX,
 };
 
+#define __CGROUPSTATS_CMD_MAX __TASKSTATS_CMD_MAX
+#define CGROUPSTATS_CMD_MAX (__CGROUPSTATS_CMD_MAX - 1)
 #define TASKSTATS_CMD_MAX (__TASKSTATS_CMD_MAX - 1)
 
 enum {
-- 
2.1.0

^ permalink raw reply related

* [PATCH 02/24] kernel: add a netlink interface to get information about tasks (v2)
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

task_diag is based on netlink sockets and looks like socket-diag, which
is used to get information about sockets.

task_diag is a new interface which is going to raplace the proc file
system in cases when we need to get information in a binary format.

A request messages is described by the task_diag_pid structure:
struct task_diag_pid {
       __u64   show_flags;
       __u64   dump_strategy;

       __u32   pid;
};

A respone is a set of netlink messages. Each message describes one task.
All task properties are divided on groups. A message contains the
TASK_DIAG_PID group, and other groups if they have been requested in
show_flags. For example, if show_flags contains TASK_DIAG_SHOW_BASE, a
response will contain the TASK_DIAG_CRED group which is described by the
task_diag_creds structure.

struct task_diag_base {
	__u32	tgid;
	__u32	pid;
	__u32	ppid;
	__u32	tpid;
	__u32	sid;
	__u32	pgid;
	__u8	state;
	char	comm[TASK_DIAG_COMM_LEN];
};

The dump_strategy field will be used in following patches to request
information for a group of processes.

v2: A few changes from David Ahern
    Use a consistent name
    Add max attr enum
    task diag: Send pid as u32
    Change _MSG/msg references to base
    Fix 8-byte alignment

Cc: David Ahern <dsahern-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 include/linux/taskstats_kern.h |   7 ++
 include/uapi/linux/task_diag.h |  60 +++++++++++++++
 include/uapi/linux/taskstats.h |   2 +
 init/Kconfig                   |  12 +++
 kernel/Makefile                |   1 +
 kernel/taskdiag.c              | 168 +++++++++++++++++++++++++++++++++++++++++
 kernel/taskstats.c             |  25 +++++-
 7 files changed, 271 insertions(+), 4 deletions(-)
 create mode 100644 include/uapi/linux/task_diag.h
 create mode 100644 kernel/taskdiag.c

diff --git a/include/linux/taskstats_kern.h b/include/linux/taskstats_kern.h
index 58de6ed..a1fd4f8 100644
--- a/include/linux/taskstats_kern.h
+++ b/include/linux/taskstats_kern.h
@@ -15,6 +15,8 @@
 extern struct kmem_cache *taskstats_cache;
 extern struct mutex taskstats_exit_mutex;
 
+extern struct genl_family taskstats_family;
+
 static inline void taskstats_tgid_free(struct signal_struct *sig)
 {
 	if (sig->stats)
@@ -23,6 +25,11 @@ static inline void taskstats_tgid_free(struct signal_struct *sig)
 
 extern void taskstats_exit(struct task_struct *, int group_dead);
 extern void taskstats_init_early(void);
+
+struct genl_info;
+struct sk_buff;
+int taskdiag_doit(struct sk_buff *skb, struct genl_info *info);
+
 #else
 static inline void taskstats_exit(struct task_struct *tsk, int group_dead)
 {}
diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
new file mode 100644
index 0000000..3a1e6c4
--- /dev/null
+++ b/include/uapi/linux/task_diag.h
@@ -0,0 +1,60 @@
+#ifndef _LINUX_TASK_DIAG_H
+#define _LINUX_TASK_DIAG_H
+
+#include <linux/types.h>
+#include <linux/capability.h>
+
+enum {
+	/* optional attributes which can be specified in show_flags */
+	TASK_DIAG_BASE	= 0,
+
+	/* other attributes */
+	TASK_DIAG_PID	= 64,	/* u32 */
+
+	__TASK_DIAG_ATTR_MAX
+#define TASK_DIAG_ATTR_MAX (__TASK_DIAG_ATTR_MAX - 1)
+};
+
+#define TASK_DIAG_SHOW_BASE	(1ULL << TASK_DIAG_BASE)
+
+enum {
+	TASK_DIAG_RUNNING,
+	TASK_DIAG_INTERRUPTIBLE,
+	TASK_DIAG_UNINTERRUPTIBLE,
+	TASK_DIAG_STOPPED,
+	TASK_DIAG_TRACE_STOP,
+	TASK_DIAG_DEAD,
+	TASK_DIAG_ZOMBIE,
+};
+
+#define TASK_DIAG_COMM_LEN 16
+
+struct task_diag_base {
+	__u32	tgid;
+	__u32	pid;
+	__u32	ppid;
+	__u32	tpid;
+	__u32	sid;
+	__u32	pgid;
+	__u8	state;
+	char	comm[TASK_DIAG_COMM_LEN];
+};
+
+#define TASK_DIAG_DUMP_ALL	0
+
+struct task_diag_pid {
+	__u64	show_flags;
+	__u64	dump_strategy;
+
+	__u32	pid;
+};
+
+enum {
+	TASK_DIAG_CMD_ATTR_UNSPEC = 0,
+	TASK_DIAG_CMD_ATTR_GET,
+	__TASK_DIAG_CMD_ATTR_MAX,
+};
+
+#define TASK_DIAG_CMD_ATTR_MAX (__TASK_DIAG_CMD_ATTR_MAX - 1)
+
+#endif /* _LINUX_TASK_DIAG_H */
diff --git a/include/uapi/linux/taskstats.h b/include/uapi/linux/taskstats.h
index a1cc91b..04b974a 100644
--- a/include/uapi/linux/taskstats.h
+++ b/include/uapi/linux/taskstats.h
@@ -181,6 +181,8 @@ enum {
 	CGROUPSTATS_CMD_GET,		/* user->kernel request/get-response */
 	CGROUPSTATS_CMD_NEW,		/* kernel->user event */
 
+	TASK_DIAG_CMD_GET,
+
 	__TASKSTATS_CMD_MAX,
 };
 
diff --git a/init/Kconfig b/init/Kconfig
index 7d1ffd2..4d0483c 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -432,6 +432,18 @@ config TASKSTATS
 
 	  Say N if unsure.
 
+config TASK_DIAG
+	bool "Export task/process properties through netlink"
+	depends on NET && TASKSTATS
+	default n
+	help
+	  Export selected properties for tasks/processes through the
+	  generic netlink interface. Unlike the proc file system, task_diag
+	  returns information in a binary format, allows to specify which
+	  information are required.
+
+	  Say N if unsure.
+
 config TASK_DELAY_ACCT
 	bool "Enable per-task delay accounting"
 	depends on TASKSTATS
diff --git a/kernel/Makefile b/kernel/Makefile
index 60c302c..ed6fed5 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
 obj-$(CONFIG_JUMP_LABEL) += jump_label.o
 obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o
 obj-$(CONFIG_TORTURE_TEST) += torture.o
+obj-$(CONFIG_TASK_DIAG) += taskdiag.o
 
 $(obj)/configs.o: $(obj)/config_data.h
 
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
new file mode 100644
index 0000000..7327e08
--- /dev/null
+++ b/kernel/taskdiag.c
@@ -0,0 +1,168 @@
+#include <linux/kernel.h>
+#include <linux/taskstats_kern.h>
+#include <linux/task_diag.h>
+#include <net/genetlink.h>
+#include <linux/pid_namespace.h>
+#include <linux/ptrace.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+
+static size_t taskdiag_packet_size(u64 show_flags)
+{
+	size_t size;
+
+	size = nla_total_size(sizeof(u32)); /* PID */
+
+	if (show_flags & TASK_DIAG_SHOW_BASE)
+		size += nla_total_size(sizeof(struct task_diag_base));
+
+	return size;
+}
+
+/*
+ * The task state array is a strange "bitmap" of
+ * reasons to sleep. Thus "running" is zero, and
+ * you can test for combinations of others with
+ * simple bit tests.
+ */
+static const __u8 task_state_array[] = {
+	TASK_DIAG_RUNNING,
+	TASK_DIAG_INTERRUPTIBLE,
+	TASK_DIAG_UNINTERRUPTIBLE,
+	TASK_DIAG_STOPPED,
+	TASK_DIAG_TRACE_STOP,
+	TASK_DIAG_DEAD,
+	TASK_DIAG_ZOMBIE,
+};
+
+static inline const __u8 get_task_state(struct task_struct *tsk)
+{
+	unsigned int state = (tsk->state | tsk->exit_state) & TASK_REPORT;
+
+	BUILD_BUG_ON(1 + ilog2(TASK_REPORT) != ARRAY_SIZE(task_state_array)-1);
+
+	return task_state_array[fls(state)];
+}
+
+static int fill_task_base(struct task_struct *p, struct sk_buff *skb)
+{
+	struct pid_namespace *ns = task_active_pid_ns(current);
+	struct task_diag_base *base;
+	struct nlattr *attr;
+	char tcomm[sizeof(p->comm)];
+	struct task_struct *tracer;
+
+	attr = nla_reserve(skb, TASK_DIAG_BASE, sizeof(struct task_diag_base));
+	if (!attr)
+		return -EMSGSIZE;
+
+	base = nla_data(attr);
+
+	rcu_read_lock();
+	base->ppid = pid_alive(p) ?
+		task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0;
+
+	base->tpid = 0;
+	tracer = ptrace_parent(p);
+	if (tracer)
+		base->tpid = task_pid_nr_ns(tracer, ns);
+
+	base->tgid = task_tgid_nr_ns(p, ns);
+	base->pid = task_pid_nr_ns(p, ns);
+	base->sid = task_session_nr_ns(p, ns);
+	base->pgid = task_pgrp_nr_ns(p, ns);
+
+	rcu_read_unlock();
+
+	get_task_comm(tcomm, p);
+	memset(base->comm, 0, TASK_DIAG_COMM_LEN);
+	strncpy(base->comm, tcomm, TASK_DIAG_COMM_LEN);
+
+	base->state = get_task_state(p);
+
+	return 0;
+}
+
+static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
+				u64 show_flags, u32 portid, u32 seq)
+{
+	void *reply;
+	int err;
+	u32 pid;
+
+	reply = genlmsg_put(skb, portid, seq, &taskstats_family, 0, TASK_DIAG_CMD_GET);
+	if (reply == NULL)
+		return -EMSGSIZE;
+
+	pid = task_pid_vnr(tsk);
+	err = nla_put_u32(skb, TASK_DIAG_PID, pid);
+	if (err)
+		goto err;
+
+	if (show_flags & TASK_DIAG_SHOW_BASE) {
+		err = fill_task_base(tsk, skb);
+		if (err)
+			goto err;
+	}
+
+	genlmsg_end(skb, reply);
+	return 0;
+err:
+	genlmsg_cancel(skb, reply);
+	return err;
+}
+
+int taskdiag_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *nla = info->attrs[TASK_DIAG_CMD_ATTR_GET];
+	struct task_struct *tsk = NULL;
+	struct task_diag_pid req;
+	struct sk_buff *msg;
+	size_t size;
+	int rc;
+
+	if (!nla_data(nla))
+		return -EINVAL;
+
+	if (nla_len(nla) < sizeof(req))
+		return -EINVAL;
+
+	/*
+	 * use a req variable to deal with alignment issues. task_diag_pid
+	 * contains u64 elements which means extended load operations can be
+	 * used and those can require 8-byte alignment (e.g., sparc)
+	 */
+	memcpy(&req, nla_data(nla), sizeof(req));
+
+	size = taskdiag_packet_size(req.show_flags);
+	msg = genlmsg_new(size, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	rcu_read_lock();
+	tsk = find_task_by_vpid(req.pid);
+	if (tsk)
+		get_task_struct(tsk);
+	rcu_read_unlock();
+	if (!tsk) {
+		rc = -ESRCH;
+		goto err;
+	};
+
+	if (!ptrace_may_access(tsk, PTRACE_MODE_READ)) {
+		put_task_struct(tsk);
+		rc = -EPERM;
+		goto err;
+	}
+
+	rc = task_diag_fill(tsk, msg, req.show_flags,
+				info->snd_portid, info->snd_seq);
+	put_task_struct(tsk);
+	if (rc < 0)
+		goto err;
+
+	return genlmsg_reply(msg, info);
+err:
+	nlmsg_free(msg);
+	return rc;
+}
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 21f82c2..d70f1e5 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -18,6 +18,7 @@
 
 #include <linux/kernel.h>
 #include <linux/taskstats_kern.h>
+#include <linux/task_diag.h>
 #include <linux/tsacct_kern.h>
 #include <linux/delayacct.h>
 #include <linux/cpumask.h>
@@ -41,7 +42,7 @@ static DEFINE_PER_CPU(__u32, taskstats_seqnum);
 static int family_registered;
 struct kmem_cache *taskstats_cache;
 
-static struct genl_family family = {
+struct genl_family taskstats_family = {
 	.id		= GENL_ID_GENERATE,
 	.name		= TASKSTATS_GENL_NAME,
 	.version	= TASKSTATS_GENL_VERSION,
@@ -92,9 +93,9 @@ static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp,
 	if (!info) {
 		int seq = this_cpu_inc_return(taskstats_seqnum) - 1;
 
-		reply = genlmsg_put(skb, 0, seq, &family, 0, cmd);
+		reply = genlmsg_put(skb, 0, seq, &taskstats_family, 0, cmd);
 	} else
-		reply = genlmsg_put_reply(skb, info, &family, 0, cmd);
+		reply = genlmsg_put_reply(skb, info, &taskstats_family, 0, cmd);
 	if (reply == NULL) {
 		nlmsg_free(skb);
 		return -EINVAL;
@@ -664,6 +665,15 @@ err:
 	nlmsg_free(rep_skb);
 }
 
+#ifdef CONFIG_TASK_DIAG
+static const struct nla_policy
+			taskdiag_cmd_get_policy[TASK_DIAG_CMD_ATTR_MAX+1] = {
+	[TASK_DIAG_CMD_ATTR_GET]  = {	.type = NLA_UNSPEC,
+					.len = sizeof(struct task_diag_pid)
+				},
+};
+#endif
+
 static const struct genl_ops taskstats_ops[] = {
 	{
 		.cmd		= TASKSTATS_CMD_GET,
@@ -676,6 +686,13 @@ static const struct genl_ops taskstats_ops[] = {
 		.doit		= cgroupstats_user_cmd,
 		.policy		= cgroupstats_cmd_get_policy,
 	},
+#ifdef CONFIG_TASK_DIAG
+	{
+		.cmd		= TASK_DIAG_CMD_GET,
+		.doit		= taskdiag_doit,
+		.policy		= taskdiag_cmd_get_policy,
+	},
+#endif
 };
 
 /* Needed early in initialization */
@@ -694,7 +711,7 @@ static int __init taskstats_init(void)
 {
 	int rc;
 
-	rc = genl_register_family_with_ops(&family, taskstats_ops);
+	rc = genl_register_family_with_ops(&taskstats_family, taskstats_ops);
 	if (rc)
 		return rc;
 
-- 
2.1.0

^ permalink raw reply related

* [PATCH 03/24] kernel: make taskstats available from all net namespaces
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 kernel/taskstats.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index d70f1e5..0c90d11 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -47,6 +47,7 @@ struct genl_family taskstats_family = {
 	.name		= TASKSTATS_GENL_NAME,
 	.version	= TASKSTATS_GENL_VERSION,
 	.maxattr	= TASKSTATS_CMD_ATTR_MAX,
+	.netnsok	= true,
 };
 
 static const struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] = {
-- 
2.1.0

^ permalink raw reply related

* [PATCH 04/24] kernel: move next_tgid from fs/proc
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

This function will be used in task_diag.

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 fs/proc/base.c          | 43 -------------------------------------------
 include/linux/proc_fs.h |  7 +++++++
 kernel/pid.c            | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 43 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 1d540b3..88cf6ae 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2994,49 +2994,6 @@ out:
 	return ERR_PTR(result);
 }
 
-/*
- * Find the first task with tgid >= tgid
- *
- */
-struct tgid_iter {
-	unsigned int tgid;
-	struct task_struct *task;
-};
-static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter)
-{
-	struct pid *pid;
-
-	if (iter.task)
-		put_task_struct(iter.task);
-	rcu_read_lock();
-retry:
-	iter.task = NULL;
-	pid = find_ge_pid(iter.tgid, ns);
-	if (pid) {
-		iter.tgid = pid_nr_ns(pid, ns);
-		iter.task = pid_task(pid, PIDTYPE_PID);
-		/* What we to know is if the pid we have find is the
-		 * pid of a thread_group_leader.  Testing for task
-		 * being a thread_group_leader is the obvious thing
-		 * todo but there is a window when it fails, due to
-		 * the pid transfer logic in de_thread.
-		 *
-		 * So we perform the straight forward test of seeing
-		 * if the pid we have found is the pid of a thread
-		 * group leader, and don't worry if the task we have
-		 * found doesn't happen to be a thread group leader.
-		 * As we don't care in the case of readdir.
-		 */
-		if (!iter.task || !has_group_leader_pid(iter.task)) {
-			iter.tgid += 1;
-			goto retry;
-		}
-		get_task_struct(iter.task);
-	}
-	rcu_read_unlock();
-	return iter;
-}
-
 #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2)
 
 /* for the /proc/ directory itself, after non-process stuff has been done */
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index b97bf2e..136b6ed 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -82,4 +82,11 @@ static inline struct proc_dir_entry *proc_net_mkdir(
 	return proc_mkdir_data(name, 0, parent, net);
 }
 
+struct tgid_iter {
+	unsigned int tgid;
+	struct task_struct *task;
+};
+
+struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter);
+
 #endif /* _LINUX_PROC_FS_H */
diff --git a/kernel/pid.c b/kernel/pid.c
index 4fd07d5..2acb0a9 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -569,6 +569,45 @@ struct pid *find_ge_pid(int nr, struct pid_namespace *ns)
 }
 
 /*
+ * Find the first task with tgid >= tgid
+ *
+ */
+struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter)
+{
+	struct pid *pid;
+
+	if (iter.task)
+		put_task_struct(iter.task);
+	rcu_read_lock();
+retry:
+	iter.task = NULL;
+	pid = find_ge_pid(iter.tgid, ns);
+	if (pid) {
+		iter.tgid = pid_nr_ns(pid, ns);
+		iter.task = pid_task(pid, PIDTYPE_PID);
+		/* What we to know is if the pid we have find is the
+		 * pid of a thread_group_leader.  Testing for task
+		 * being a thread_group_leader is the obvious thing
+		 * todo but there is a window when it fails, due to
+		 * the pid transfer logic in de_thread.
+		 *
+		 * So we perform the straight forward test of seeing
+		 * if the pid we have found is the pid of a thread
+		 * group leader, and don't worry if the task we have
+		 * found doesn't happen to be a thread group leader.
+		 * As we don't care in the case of readdir.
+		 */
+		if (!iter.task || !has_group_leader_pid(iter.task)) {
+			iter.tgid += 1;
+			goto retry;
+		}
+		get_task_struct(iter.task);
+	}
+	rcu_read_unlock();
+	return iter;
+}
+
+/*
  * The pid hash table is scaled according to the amount of memory in the
  * machine.  From a minimum of 16 slots up to 4096 slots at one gigabyte or
  * more.
-- 
2.1.0

^ permalink raw reply related

* [PATCH 05/24] task_diag: add ability to get information about all tasks
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

For that we need to set NLM_F_DUMP. A response contain on a few packets.

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 include/linux/taskstats_kern.h |  2 ++
 kernel/taskdiag.c              | 40 ++++++++++++++++++++++++++++++++++++++++
 kernel/taskstats.c             |  1 +
 3 files changed, 43 insertions(+)

diff --git a/include/linux/taskstats_kern.h b/include/linux/taskstats_kern.h
index a1fd4f8..716835f 100644
--- a/include/linux/taskstats_kern.h
+++ b/include/linux/taskstats_kern.h
@@ -29,6 +29,8 @@ extern void taskstats_init_early(void);
 struct genl_info;
 struct sk_buff;
 int taskdiag_doit(struct sk_buff *skb, struct genl_info *info);
+struct netlink_callback;
+int taskdiag_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
 
 #else
 static inline void taskstats_exit(struct task_struct *tsk, int group_dead)
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index 7327e08..3497304 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c
@@ -112,6 +112,46 @@ err:
 	return err;
 }
 
+int taskdiag_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct pid_namespace *ns = task_active_pid_ns(current);
+	struct tgid_iter iter;
+	struct nlattr *na;
+	struct task_diag_pid req;
+	int rc;
+
+	if (nlmsg_len(cb->nlh) < GENL_HDRLEN + sizeof(req))
+		return -EINVAL;
+
+	na = nlmsg_data(cb->nlh) + GENL_HDRLEN;
+	if (na->nla_type < 0)
+		return -EINVAL;
+
+	memcpy(&req, nla_data(na), sizeof(req));
+
+	iter.tgid = cb->args[0];
+	iter.task = NULL;
+	for (iter = next_tgid(ns, iter);
+	     iter.task;
+	     iter.tgid += 1, iter = next_tgid(ns, iter)) {
+		if (!ptrace_may_access(iter.task, PTRACE_MODE_READ))
+			continue;
+
+		rc = task_diag_fill(iter.task, skb, req.show_flags,
+				NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq);
+		if (rc < 0) {
+			put_task_struct(iter.task);
+			if (rc != -EMSGSIZE)
+				return rc;
+			break;
+		}
+	}
+
+	cb->args[0] = iter.tgid;
+
+	return skb->len;
+}
+
 int taskdiag_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct nlattr *nla = info->attrs[TASK_DIAG_CMD_ATTR_GET];
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 0c90d11..40f2cdf2 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -691,6 +691,7 @@ static const struct genl_ops taskstats_ops[] = {
 	{
 		.cmd		= TASK_DIAG_CMD_GET,
 		.doit		= taskdiag_doit,
+		.dumpit		= taskdiag_dumpit,
 		.policy		= taskdiag_cmd_get_policy,
 	},
 #endif
-- 
2.1.0

^ permalink raw reply related

* [PATCH 06/24] task_diag: add ability to split per-task data on a few netlink messages
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

The size of requested per-task data can be bigger that the size of one
netlink skb. Currently we are able to split data into a few netlink
messages.

v2: set NLM_F_MULTI for multipart packets

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 kernel/taskdiag.c | 76 +++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 51 insertions(+), 25 deletions(-)

diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index 3497304..5771baf 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c
@@ -84,13 +84,21 @@ static int fill_task_base(struct task_struct *p, struct sk_buff *skb)
 }
 
 static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
-				u64 show_flags, u32 portid, u32 seq)
+				u64 show_flags, u32 portid, u32 seq,
+				struct netlink_callback *cb)
 {
 	void *reply;
-	int err;
+	int err = 0, i = 0, n = 0;
+	int flags = 0;
 	u32 pid;
 
-	reply = genlmsg_put(skb, portid, seq, &taskstats_family, 0, TASK_DIAG_CMD_GET);
+	if (cb) {
+		n = cb->args[2];
+		flags |= NLM_F_MULTI;
+	}
+
+	reply = genlmsg_put(skb, portid, seq, &taskstats_family,
+					flags, TASK_DIAG_CMD_GET);
 	if (reply == NULL)
 		return -EMSGSIZE;
 
@@ -100,15 +108,26 @@ static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
 		goto err;
 
 	if (show_flags & TASK_DIAG_SHOW_BASE) {
-		err = fill_task_base(tsk, skb);
+		if (i >= n)
+			err = fill_task_base(tsk, skb);
 		if (err)
 			goto err;
+		i++;
 	}
 
 	genlmsg_end(skb, reply);
+	if (cb)
+		cb->args[2] = 0;
+
 	return 0;
 err:
-	genlmsg_cancel(skb, reply);
+	if (err == -EMSGSIZE && i != 0) {
+		if (cb)
+			cb->args[2] = i;
+		genlmsg_end(skb, reply);
+	} else
+		genlmsg_cancel(skb, reply);
+
 	return err;
 }
 
@@ -138,7 +157,7 @@ int taskdiag_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 			continue;
 
 		rc = task_diag_fill(iter.task, skb, req.show_flags,
-				NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq);
+				NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb);
 		if (rc < 0) {
 			put_task_struct(iter.task);
 			if (rc != -EMSGSIZE)
@@ -157,7 +176,7 @@ int taskdiag_doit(struct sk_buff *skb, struct genl_info *info)
 	struct nlattr *nla = info->attrs[TASK_DIAG_CMD_ATTR_GET];
 	struct task_struct *tsk = NULL;
 	struct task_diag_pid req;
-	struct sk_buff *msg;
+	struct sk_buff *msg = NULL;
 	size_t size;
 	int rc;
 
@@ -174,35 +193,42 @@ int taskdiag_doit(struct sk_buff *skb, struct genl_info *info)
 	 */
 	memcpy(&req, nla_data(nla), sizeof(req));
 
-	size = taskdiag_packet_size(req.show_flags);
-	msg = genlmsg_new(size, GFP_KERNEL);
-	if (!msg)
-		return -ENOMEM;
-
 	rcu_read_lock();
 	tsk = find_task_by_vpid(req.pid);
 	if (tsk)
 		get_task_struct(tsk);
 	rcu_read_unlock();
-	if (!tsk) {
-		rc = -ESRCH;
-		goto err;
-	};
+	if (!tsk)
+		return -ESRCH;
 
 	if (!ptrace_may_access(tsk, PTRACE_MODE_READ)) {
 		put_task_struct(tsk);
-		rc = -EPERM;
-		goto err;
+		return -EPERM;
+	}
+
+	size = taskdiag_packet_size(req.show_flags);
+
+	while (1) {
+		msg = genlmsg_new(size, GFP_KERNEL);
+		if (!msg) {
+			put_task_struct(tsk);
+			return -EMSGSIZE;
+		}
+
+		rc = task_diag_fill(tsk, msg, req.show_flags,
+					info->snd_portid, info->snd_seq, NULL);
+		if (rc != -EMSGSIZE)
+			break;
+
+		nlmsg_free(msg);
+		size += 128;
 	}
 
-	rc = task_diag_fill(tsk, msg, req.show_flags,
-				info->snd_portid, info->snd_seq);
 	put_task_struct(tsk);
-	if (rc < 0)
-		goto err;
+	if (rc < 0) {
+		nlmsg_free(msg);
+		return rc;
+	}
 
 	return genlmsg_reply(msg, info);
-err:
-	nlmsg_free(msg);
-	return rc;
 }
-- 
2.1.0

^ permalink raw reply related

* [PATCH 07/24] task_diag: add a new group to get process credentials
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

A response is represented by the task_diag_creds structure:

struct task_diag_creds {
       struct task_diag_caps cap_inheritable;
       struct task_diag_caps cap_permitted;
       struct task_diag_caps cap_effective;
       struct task_diag_caps cap_bset;

       __u32 uid;
       __u32 euid;
       __u32 suid;
       __u32 fsuid;
       __u32 gid;
       __u32 egid;
       __u32 sgid;
       __u32 fsgid;
};

This group is optional and it's filled only if show_flags contains
TASK_DIAG_SHOW_CRED.

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 include/uapi/linux/task_diag.h | 22 ++++++++++++++++++
 kernel/taskdiag.c              | 53 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
index 3a1e6c4..0e659d6 100644
--- a/include/uapi/linux/task_diag.h
+++ b/include/uapi/linux/task_diag.h
@@ -7,6 +7,7 @@
 enum {
 	/* optional attributes which can be specified in show_flags */
 	TASK_DIAG_BASE	= 0,
+	TASK_DIAG_CRED,
 
 	/* other attributes */
 	TASK_DIAG_PID	= 64,	/* u32 */
@@ -16,6 +17,7 @@ enum {
 };
 
 #define TASK_DIAG_SHOW_BASE	(1ULL << TASK_DIAG_BASE)
+#define TASK_DIAG_SHOW_CRED	(1ULL << TASK_DIAG_CRED)
 
 enum {
 	TASK_DIAG_RUNNING,
@@ -40,6 +42,26 @@ struct task_diag_base {
 	char	comm[TASK_DIAG_COMM_LEN];
 };
 
+struct task_diag_caps {
+	__u32 cap[_LINUX_CAPABILITY_U32S_3];
+};
+
+struct task_diag_creds {
+	struct task_diag_caps cap_inheritable;
+	struct task_diag_caps cap_permitted;
+	struct task_diag_caps cap_effective;
+	struct task_diag_caps cap_bset;
+
+	__u32 uid;
+	__u32 euid;
+	__u32 suid;
+	__u32 fsuid;
+	__u32 gid;
+	__u32 egid;
+	__u32 sgid;
+	__u32 fsgid;
+};
+
 #define TASK_DIAG_DUMP_ALL	0
 
 struct task_diag_pid {
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index 5771baf..5123e12 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c
@@ -16,6 +16,9 @@ static size_t taskdiag_packet_size(u64 show_flags)
 	if (show_flags & TASK_DIAG_SHOW_BASE)
 		size += nla_total_size(sizeof(struct task_diag_base));
 
+	if (show_flags & TASK_DIAG_SHOW_CRED)
+		size += nla_total_size(sizeof(struct task_diag_creds));
+
 	return size;
 }
 
@@ -83,6 +86,48 @@ static int fill_task_base(struct task_struct *p, struct sk_buff *skb)
 	return 0;
 }
 
+static inline void caps2diag(struct task_diag_caps *diag, const kernel_cap_t *cap)
+{
+	int i;
+
+	for (i = 0; i < _LINUX_CAPABILITY_U32S_3; i++)
+		diag->cap[i] = cap->cap[i];
+}
+
+static int fill_creds(struct task_struct *p, struct sk_buff *skb)
+{
+	struct user_namespace *user_ns = current_user_ns();
+	struct task_diag_creds *diag_cred;
+	const struct cred *cred;
+	struct nlattr *attr;
+
+	attr = nla_reserve(skb, TASK_DIAG_CRED, sizeof(struct task_diag_creds));
+	if (!attr)
+		return -EMSGSIZE;
+
+	diag_cred = nla_data(attr);
+
+	cred = get_task_cred(p);
+
+	caps2diag(&diag_cred->cap_inheritable, &cred->cap_inheritable);
+	caps2diag(&diag_cred->cap_permitted, &cred->cap_permitted);
+	caps2diag(&diag_cred->cap_effective, &cred->cap_effective);
+	caps2diag(&diag_cred->cap_bset, &cred->cap_bset);
+
+	diag_cred->uid   = from_kuid_munged(user_ns, cred->uid);
+	diag_cred->euid  = from_kuid_munged(user_ns, cred->euid);
+	diag_cred->suid  = from_kuid_munged(user_ns, cred->suid);
+	diag_cred->fsuid = from_kuid_munged(user_ns, cred->fsuid);
+	diag_cred->gid   = from_kgid_munged(user_ns, cred->gid);
+	diag_cred->egid  = from_kgid_munged(user_ns, cred->egid);
+	diag_cred->sgid  = from_kgid_munged(user_ns, cred->sgid);
+	diag_cred->fsgid = from_kgid_munged(user_ns, cred->fsgid);
+
+	put_cred(cred);
+
+	return 0;
+}
+
 static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
 				u64 show_flags, u32 portid, u32 seq,
 				struct netlink_callback *cb)
@@ -115,6 +160,14 @@ static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
 		i++;
 	}
 
+	if (show_flags & TASK_DIAG_SHOW_CRED) {
+		if (i >= n)
+			err = fill_creds(tsk, skb);
+		if (err)
+			goto err;
+		i++;
+	}
+
 	genlmsg_end(skb, reply);
 	if (cb)
 		cb->args[2] = 0;
-- 
2.1.0

^ permalink raw reply related

* [PATCH 08/24] proc: pick out a function to iterate task children
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

This function will be used in task_diag.

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 fs/proc/array.c | 67 ++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 40 insertions(+), 27 deletions(-)

diff --git a/fs/proc/array.c b/fs/proc/array.c
index ce065cf..52c4a7b 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -578,31 +578,26 @@ int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
 }
 
 #ifdef CONFIG_PROC_CHILDREN
-static struct pid *
-get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)
+static struct task_struct *
+task_next_child(struct task_struct *parent, struct task_struct *prev, unsigned int pos)
 {
-	struct task_struct *start, *task;
-	struct pid *pid = NULL;
+	struct task_struct *task;
 
 	read_lock(&tasklist_lock);
-
-	start = pid_task(proc_pid(inode), PIDTYPE_PID);
-	if (!start)
-		goto out;
-
 	/*
 	 * Lets try to continue searching first, this gives
 	 * us significant speedup on children-rich processes.
 	 */
-	if (pid_prev) {
-		task = pid_task(pid_prev, PIDTYPE_PID);
-		if (task && task->real_parent == start &&
+	if (prev) {
+		task = prev;
+		if (task && task->real_parent == parent &&
 		    !(list_empty(&task->sibling))) {
-			if (list_is_last(&task->sibling, &start->children))
+			if (list_is_last(&task->sibling, &parent->children)) {
+				task = NULL;
 				goto out;
+			}
 			task = list_first_entry(&task->sibling,
 						struct task_struct, sibling);
-			pid = get_pid(task_pid(task));
 			goto out;
 		}
 	}
@@ -622,16 +617,34 @@ get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)
 	 * So one need to stop or freeze the leader and all
 	 * its children to get a precise result.
 	 */
-	list_for_each_entry(task, &start->children, sibling) {
-		if (pos-- == 0) {
-			pid = get_pid(task_pid(task));
-			break;
-		}
+	list_for_each_entry(task, &parent->children, sibling) {
+		if (pos-- == 0)
+			goto out;
 	}
-
+	task = NULL;
 out:
+	if (prev)
+		put_task_struct(prev);
+	if (task)
+		get_task_struct(task);
 	read_unlock(&tasklist_lock);
-	return pid;
+	return task;
+}
+
+static struct task_struct *
+get_children_pid(struct inode *inode, struct task_struct *prev, loff_t pos)
+{
+	struct task_struct *start, *task = NULL;
+
+	start = get_proc_task(inode);
+	if (!start)
+		goto out;
+
+	task = task_next_child(start, prev, pos);
+
+	put_task_struct(start);
+out:
+	return task;
 }
 
 static int children_seq_show(struct seq_file *seq, void *v)
@@ -639,7 +652,7 @@ static int children_seq_show(struct seq_file *seq, void *v)
 	struct inode *inode = seq->private;
 	pid_t pid;
 
-	pid = pid_nr_ns(v, inode->i_sb->s_fs_info);
+	pid = task_pid_nr_ns(v, inode->i_sb->s_fs_info);
 	seq_printf(seq, "%d ", pid);
 
 	return 0;
@@ -652,18 +665,18 @@ static void *children_seq_start(struct seq_file *seq, loff_t *pos)
 
 static void *children_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
-	struct pid *pid;
+	struct task_struct *task;
 
-	pid = get_children_pid(seq->private, v, *pos + 1);
-	put_pid(v);
+	task = get_children_pid(seq->private, v, *pos + 1);
 
 	++*pos;
-	return pid;
+	return task;
 }
 
 static void children_seq_stop(struct seq_file *seq, void *v)
 {
-	put_pid(v);
+	if (v)
+		put_task_struct(v);
 }
 
 static const struct seq_operations children_seq_ops = {
-- 
2.1.0

^ permalink raw reply related

* [PATCH 09/24] proc: move task_next_child() from fs/proc
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

It's going to be used in task_diag.

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 fs/proc/array.c         | 53 -------------------------------------------------
 include/linux/proc_fs.h |  3 +++
 kernel/pid.c            | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 53 deletions(-)

diff --git a/fs/proc/array.c b/fs/proc/array.c
index 52c4a7b..e22266d 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -579,59 +579,6 @@ int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
 
 #ifdef CONFIG_PROC_CHILDREN
 static struct task_struct *
-task_next_child(struct task_struct *parent, struct task_struct *prev, unsigned int pos)
-{
-	struct task_struct *task;
-
-	read_lock(&tasklist_lock);
-	/*
-	 * Lets try to continue searching first, this gives
-	 * us significant speedup on children-rich processes.
-	 */
-	if (prev) {
-		task = prev;
-		if (task && task->real_parent == parent &&
-		    !(list_empty(&task->sibling))) {
-			if (list_is_last(&task->sibling, &parent->children)) {
-				task = NULL;
-				goto out;
-			}
-			task = list_first_entry(&task->sibling,
-						struct task_struct, sibling);
-			goto out;
-		}
-	}
-
-	/*
-	 * Slow search case.
-	 *
-	 * We might miss some children here if children
-	 * are exited while we were not holding the lock,
-	 * but it was never promised to be accurate that
-	 * much.
-	 *
-	 * "Just suppose that the parent sleeps, but N children
-	 *  exit after we printed their tids. Now the slow paths
-	 *  skips N extra children, we miss N tasks." (c)
-	 *
-	 * So one need to stop or freeze the leader and all
-	 * its children to get a precise result.
-	 */
-	list_for_each_entry(task, &parent->children, sibling) {
-		if (pos-- == 0)
-			goto out;
-	}
-	task = NULL;
-out:
-	if (prev)
-		put_task_struct(prev);
-	if (task)
-		get_task_struct(task);
-	read_unlock(&tasklist_lock);
-	return task;
-}
-
-static struct task_struct *
 get_children_pid(struct inode *inode, struct task_struct *prev, loff_t pos)
 {
 	struct task_struct *start, *task = NULL;
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 136b6ed..497e58c 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -89,4 +89,7 @@ struct tgid_iter {
 
 struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter);
 
+struct task_struct *
+task_next_child(struct task_struct *parent, struct task_struct *prev, unsigned int pos);
+
 #endif /* _LINUX_PROC_FS_H */
diff --git a/kernel/pid.c b/kernel/pid.c
index 2acb0a9..42a6b40 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -607,6 +607,59 @@ retry:
 	return iter;
 }
 
+struct task_struct *
+task_next_child(struct task_struct *parent, struct task_struct *prev, unsigned int pos)
+{
+	struct task_struct *task;
+
+	read_lock(&tasklist_lock);
+	/*
+	 * Lets try to continue searching first, this gives
+	 * us significant speedup on children-rich processes.
+	 */
+	if (prev) {
+		task = prev;
+		if (task && task->real_parent == parent &&
+		    !(list_empty(&task->sibling))) {
+			if (list_is_last(&task->sibling, &parent->children)) {
+				task = NULL;
+				goto out;
+			}
+			task = list_first_entry(&task->sibling,
+						struct task_struct, sibling);
+			goto out;
+		}
+	}
+
+	/*
+	 * Slow search case.
+	 *
+	 * We might miss some children here if children
+	 * are exited while we were not holding the lock,
+	 * but it was never promised to be accurate that
+	 * much.
+	 *
+	 * "Just suppose that the parent sleeps, but N children
+	 *  exit after we printed their tids. Now the slow paths
+	 *  skips N extra children, we miss N tasks." (c)
+	 *
+	 * So one need to stop or freeze the leader and all
+	 * its children to get a precise result.
+	 */
+	list_for_each_entry(task, &parent->children, sibling) {
+		if (pos-- == 0)
+			goto out;
+	}
+	task = NULL;
+out:
+	if (prev)
+		put_task_struct(prev);
+	if (task)
+		get_task_struct(task);
+	read_unlock(&tasklist_lock);
+	return task;
+}
+
 /*
  * The pid hash table is scaled according to the amount of memory in the
  * machine.  From a minimum of 16 slots up to 4096 slots at one gigabyte or
-- 
2.1.0

^ permalink raw reply related

* [PATCH 10/24] task_diag: add ability to dump children (v2)
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

Now we can dump all task or children of a specified task.
It's an example how this interface can be expanded for different
use-cases.

v2: Fixes from David Ahern
    Add missing break in iter_stop
    Fix 8-byte alignment issues

Cc: David Ahern <dsahern-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 include/uapi/linux/task_diag.h |   1 +
 kernel/taskdiag.c              | 117 +++++++++++++++++++++++++++++++++++------
 2 files changed, 102 insertions(+), 16 deletions(-)

diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
index 0e659d6..af192db 100644
--- a/include/uapi/linux/task_diag.h
+++ b/include/uapi/linux/task_diag.h
@@ -63,6 +63,7 @@ struct task_diag_creds {
 };
 
 #define TASK_DIAG_DUMP_ALL	0
+#define TASK_DIAG_DUMP_CHILDREN	1
 
 struct task_diag_pid {
 	__u64	show_flags;
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index 5123e12..6dd3361 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c
@@ -184,42 +184,127 @@ err:
 	return err;
 }
 
+struct task_iter {
+	struct task_diag_pid req;
+	struct pid_namespace *ns;
+	struct netlink_callback *cb;
+	struct task_struct *parent;
+
+	union {
+		struct tgid_iter tgid;
+		struct {
+			unsigned int		pos;
+			struct task_struct	*task;
+		};
+	};
+};
+
+static void iter_stop(struct task_iter *iter)
+{
+	struct task_struct *task;
+
+	if (iter->parent)
+		put_task_struct(iter->parent);
+
+	switch (iter->req.dump_strategy) {
+	case TASK_DIAG_DUMP_ALL:
+		task = iter->tgid.task;
+		break;
+	default:
+		task = iter->task;
+	}
+	if (task)
+		put_task_struct(task);
+}
+
+static struct task_struct *iter_start(struct task_iter *iter)
+{
+	if (iter->req.pid > 0) {
+		rcu_read_lock();
+		iter->parent = find_task_by_pid_ns(iter->req.pid, iter->ns);
+		if (iter->parent)
+			get_task_struct(iter->parent);
+		rcu_read_unlock();
+	}
+
+	switch (iter->req.dump_strategy) {
+	case TASK_DIAG_DUMP_CHILDREN:
+
+		if (iter->parent == NULL)
+			return ERR_PTR(-ESRCH);
+
+		iter->pos = iter->cb->args[0];
+		iter->task = task_next_child(iter->parent, NULL, iter->pos);
+		return iter->task;
+
+	case TASK_DIAG_DUMP_ALL:
+		iter->tgid.tgid = iter->cb->args[0];
+		iter->tgid.task = NULL;
+		iter->tgid = next_tgid(iter->ns, iter->tgid);
+		return iter->tgid.task;
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
+static struct task_struct *iter_next(struct task_iter *iter)
+{
+	switch (iter->req.dump_strategy) {
+	case TASK_DIAG_DUMP_CHILDREN:
+		iter->pos++;
+		iter->task = task_next_child(iter->parent, iter->task, iter->pos);
+		iter->cb->args[0] = iter->pos;
+		return iter->task;
+
+	case TASK_DIAG_DUMP_ALL:
+		iter->tgid.tgid += 1;
+		iter->tgid = next_tgid(iter->ns, iter->tgid);
+		iter->cb->args[0] = iter->tgid.tgid;
+		return iter->tgid.task;
+	}
+
+	return NULL;
+}
+
 int taskdiag_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct pid_namespace *ns = task_active_pid_ns(current);
-	struct tgid_iter iter;
+	struct task_iter iter;
 	struct nlattr *na;
-	struct task_diag_pid req;
+	struct task_struct *task;
 	int rc;
 
-	if (nlmsg_len(cb->nlh) < GENL_HDRLEN + sizeof(req))
+	if (nlmsg_len(cb->nlh) < GENL_HDRLEN + sizeof(iter.req))
 		return -EINVAL;
 
 	na = nlmsg_data(cb->nlh) + GENL_HDRLEN;
 	if (na->nla_type < 0)
 		return -EINVAL;
 
-	memcpy(&req, nla_data(na), sizeof(req));
+	memcpy(&iter.req, nla_data(na), sizeof(iter.req));
 
-	iter.tgid = cb->args[0];
-	iter.task = NULL;
-	for (iter = next_tgid(ns, iter);
-	     iter.task;
-	     iter.tgid += 1, iter = next_tgid(ns, iter)) {
-		if (!ptrace_may_access(iter.task, PTRACE_MODE_READ))
-			continue;
+	iter.ns     = ns;
+	iter.cb     = cb;
+	iter.parent = NULL;
 
-		rc = task_diag_fill(iter.task, skb, req.show_flags,
+	task = iter_start(&iter);
+	if (IS_ERR(task))
+		return PTR_ERR(task);
+
+	for (; task; task = iter_next(&iter)) {
+		if (!ptrace_may_access(task, PTRACE_MODE_READ))
+			continue;
+		rc = task_diag_fill(task, skb, iter.req.show_flags,
 				NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb);
 		if (rc < 0) {
-			put_task_struct(iter.task);
-			if (rc != -EMSGSIZE)
+			if (rc != -EMSGSIZE) {
+				iter_stop(&iter);
 				return rc;
+			}
 			break;
 		}
 	}
-
-	cb->args[0] = iter.tgid;
+	iter_stop(&iter);
 
 	return skb->len;
 }
-- 
2.1.0

^ permalink raw reply related

* [PATCH 11/24] task_diag: add a new group to get task statistics
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, Andrey Vagin, Oleg Nesterov,
	Andrew Morton, Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi,
	Arnd Bergmann, Arnaldo Carvalho de Melo, David Ahern,
	Andy Lutomirski, Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

Signed-off-by: Andrey Vagin <avagin-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 include/linux/taskstats_kern.h |  2 ++
 include/uapi/linux/task_diag.h |  2 ++
 kernel/taskdiag.c              | 30 ++++++++++++++++++++++++++++++
 kernel/taskstats.c             |  2 +-
 4 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/include/linux/taskstats_kern.h b/include/linux/taskstats_kern.h
index 716835f..8faec32 100644
--- a/include/linux/taskstats_kern.h
+++ b/include/linux/taskstats_kern.h
@@ -32,6 +32,8 @@ int taskdiag_doit(struct sk_buff *skb, struct genl_info *info);
 struct netlink_callback;
 int taskdiag_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
 
+extern int fill_stats_for_pid(pid_t pid, struct taskstats *stats);
+
 #else
 static inline void taskstats_exit(struct task_struct *tsk, int group_dead)
 {}
diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
index af192db..c51380a 100644
--- a/include/uapi/linux/task_diag.h
+++ b/include/uapi/linux/task_diag.h
@@ -8,6 +8,7 @@ enum {
 	/* optional attributes which can be specified in show_flags */
 	TASK_DIAG_BASE	= 0,
 	TASK_DIAG_CRED,
+	TASK_DIAG_STAT,
 
 	/* other attributes */
 	TASK_DIAG_PID	= 64,	/* u32 */
@@ -18,6 +19,7 @@ enum {
 
 #define TASK_DIAG_SHOW_BASE	(1ULL << TASK_DIAG_BASE)
 #define TASK_DIAG_SHOW_CRED	(1ULL << TASK_DIAG_CRED)
+#define TASK_DIAG_SHOW_STAT	(1ULL << TASK_DIAG_STAT)
 
 enum {
 	TASK_DIAG_RUNNING,
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index 6dd3361..a49ccab 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c
@@ -6,6 +6,7 @@
 #include <linux/ptrace.h>
 #include <linux/proc_fs.h>
 #include <linux/sched.h>
+#include <linux/taskstats.h>
 
 static size_t taskdiag_packet_size(u64 show_flags)
 {
@@ -19,6 +20,9 @@ static size_t taskdiag_packet_size(u64 show_flags)
 	if (show_flags & TASK_DIAG_SHOW_CRED)
 		size += nla_total_size(sizeof(struct task_diag_creds));
 
+	if (show_flags & TASK_DIAG_SHOW_STAT)
+		size += nla_total_size(sizeof(struct taskstats));
+
 	return size;
 }
 
@@ -94,6 +98,24 @@ static inline void caps2diag(struct task_diag_caps *diag, const kernel_cap_t *ca
 		diag->cap[i] = cap->cap[i];
 }
 
+static int fill_stats(struct task_struct *tsk, struct sk_buff *skb)
+{
+	struct taskstats *diag_stats;
+	struct nlattr *attr;
+	int ret;
+
+	attr = nla_reserve(skb, TASK_DIAG_STAT, sizeof(struct taskstats));
+	if (!attr)
+		return -EMSGSIZE;
+
+	diag_stats = nla_data(attr);
+
+	ret = fill_stats_for_pid(task_pid_vnr(tsk), diag_stats);
+	if (ret)
+		return ret;
+	return 0;
+}
+
 static int fill_creds(struct task_struct *p, struct sk_buff *skb)
 {
 	struct user_namespace *user_ns = current_user_ns();
@@ -168,6 +190,14 @@ static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
 		i++;
 	}
 
+	if (show_flags & TASK_DIAG_SHOW_STAT) {
+		if (i >= n)
+			err = fill_stats(tsk, skb);
+		if (err)
+			goto err;
+		i++;
+	}
+
 	genlmsg_end(skb, reply);
 	if (cb)
 		cb->args[2] = 0;
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 40f2cdf2..3b3d660 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -192,7 +192,7 @@ static void fill_stats(struct user_namespace *user_ns,
 	xacct_add_tsk(stats, tsk);
 }
 
-static int fill_stats_for_pid(pid_t pid, struct taskstats *stats)
+int fill_stats_for_pid(pid_t pid, struct taskstats *stats)
 {
 	struct task_struct *tsk;
 
-- 
2.1.0

^ permalink raw reply related

* [PATCH 12/24] task_diag: add a new group to get tasks memory mappings (v2)
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-api, Andrey Vagin, Oleg Nesterov, Andrew Morton,
	Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi, Arnd Bergmann,
	Arnaldo Carvalho de Melo, David Ahern, Andy Lutomirski,
	Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin@openvz.org>

v2: Fixes from David Ahern
    * Fix 8-byte alignment
    * Change implementation of DIAG_VMA attribute:

This patch puts the filename into the task_diag_vma struct and
converts TASK_DIAG_VMA attribute into a series of task_diag_vma.
Now is there is a single TASK_DIAG_VMA attribute that is parsed
as:

 | struct task_diag_vma | filename | ...

Cc: David Ahern <dsahern@gmail.com>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
 include/uapi/linux/task_diag.h |  54 +++++++++
 kernel/taskdiag.c              | 255 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 306 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
index c51380a..943d8d1 100644
--- a/include/uapi/linux/task_diag.h
+++ b/include/uapi/linux/task_diag.h
@@ -2,6 +2,7 @@
 #define _LINUX_TASK_DIAG_H
 
 #include <linux/types.h>
+#include <linux/netlink.h>
 #include <linux/capability.h>
 
 enum {
@@ -9,6 +10,7 @@ enum {
 	TASK_DIAG_BASE	= 0,
 	TASK_DIAG_CRED,
 	TASK_DIAG_STAT,
+	TASK_DIAG_VMA,
 
 	/* other attributes */
 	TASK_DIAG_PID	= 64,	/* u32 */
@@ -20,6 +22,7 @@ enum {
 #define TASK_DIAG_SHOW_BASE	(1ULL << TASK_DIAG_BASE)
 #define TASK_DIAG_SHOW_CRED	(1ULL << TASK_DIAG_CRED)
 #define TASK_DIAG_SHOW_STAT	(1ULL << TASK_DIAG_STAT)
+#define TASK_DIAG_SHOW_VMA	(1ULL << TASK_DIAG_VMA)
 
 enum {
 	TASK_DIAG_RUNNING,
@@ -64,6 +67,57 @@ struct task_diag_creds {
 	__u32 fsgid;
 };
 
+#define TASK_DIAG_VMA_F_READ		(1ULL <<  0)
+#define TASK_DIAG_VMA_F_WRITE		(1ULL <<  1)
+#define TASK_DIAG_VMA_F_EXEC		(1ULL <<  2)
+#define TASK_DIAG_VMA_F_SHARED		(1ULL <<  3)
+#define TASK_DIAG_VMA_F_MAYREAD		(1ULL <<  4)
+#define TASK_DIAG_VMA_F_MAYWRITE	(1ULL <<  5)
+#define TASK_DIAG_VMA_F_MAYEXEC		(1ULL <<  6)
+#define TASK_DIAG_VMA_F_MAYSHARE	(1ULL <<  7)
+#define TASK_DIAG_VMA_F_GROWSDOWN	(1ULL <<  8)
+#define TASK_DIAG_VMA_F_PFNMAP		(1ULL <<  9)
+#define TASK_DIAG_VMA_F_DENYWRITE	(1ULL << 10)
+#define TASK_DIAG_VMA_F_MPX		(1ULL << 11)
+#define TASK_DIAG_VMA_F_LOCKED		(1ULL << 12)
+#define TASK_DIAG_VMA_F_IO		(1ULL << 13)
+#define TASK_DIAG_VMA_F_SEQ_READ	(1ULL << 14)
+#define TASK_DIAG_VMA_F_RAND_READ	(1ULL << 15)
+#define TASK_DIAG_VMA_F_DONTCOPY	(1ULL << 16)
+#define TASK_DIAG_VMA_F_DONTEXPAND	(1ULL << 17)
+#define TASK_DIAG_VMA_F_ACCOUNT		(1ULL << 18)
+#define TASK_DIAG_VMA_F_NORESERVE	(1ULL << 19)
+#define TASK_DIAG_VMA_F_HUGETLB		(1ULL << 20)
+#define TASK_DIAG_VMA_F_ARCH_1		(1ULL << 21)
+#define TASK_DIAG_VMA_F_DONTDUMP	(1ULL << 22)
+#define TASK_DIAG_VMA_F_SOFTDIRTY	(1ULL << 23)
+#define TASK_DIAG_VMA_F_MIXEDMAP	(1ULL << 24)
+#define TASK_DIAG_VMA_F_HUGEPAGE	(1ULL << 25)
+#define TASK_DIAG_VMA_F_NOHUGEPAGE	(1ULL << 26)
+#define TASK_DIAG_VMA_F_MERGEABLE	(1ULL << 27)
+
+/* task_diag_vma must be NLA_ALIGN'ed */
+struct task_diag_vma {
+	__u64 start, end;
+	__u64 vm_flags;
+	__u64 pgoff;
+	__u32 major;
+	__u32 minor;
+	__u64 inode;
+	__u32 generation;
+	__u16 vma_len;
+	__u16 name_off;
+	__u16 name_len;
+} __attribute__((__aligned__(NLA_ALIGNTO)));
+
+static inline char *task_diag_vma_name(struct task_diag_vma *vma)
+{
+	if (!vma->name_len)
+		return NULL;
+
+	return ((char *)vma) + vma->name_off;
+}
+
 #define TASK_DIAG_DUMP_ALL	0
 #define TASK_DIAG_DUMP_CHILDREN	1
 
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index a49ccab..c488c1b 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c
@@ -8,7 +8,7 @@
 #include <linux/sched.h>
 #include <linux/taskstats.h>
 
-static size_t taskdiag_packet_size(u64 show_flags)
+static size_t taskdiag_packet_size(u64 show_flags, int n_vma)
 {
 	size_t size;
 
@@ -23,6 +23,14 @@ static size_t taskdiag_packet_size(u64 show_flags)
 	if (show_flags & TASK_DIAG_SHOW_STAT)
 		size += nla_total_size(sizeof(struct taskstats));
 
+	if (show_flags & TASK_DIAG_SHOW_VMA && n_vma > 0) {
+		/*
+		 * 128 is a schwag on average path length for maps; used to
+		 * ballpark initial memory allocation for genl msg
+		 */
+		size += nla_total_size(sizeof(struct task_diag_vma) * n_vma + 128);
+	}
+
 	return size;
 }
 
@@ -150,12 +158,245 @@ static int fill_creds(struct task_struct *p, struct sk_buff *skb)
 	return 0;
 }
 
+static u64 get_vma_flags(struct vm_area_struct *vma)
+{
+	u64 flags = 0;
+
+	static const u64 mnemonics[BITS_PER_LONG] = {
+		/*
+		 * In case if we meet a flag we don't know about.
+		 */
+		[0 ... (BITS_PER_LONG-1)] = 0,
+
+		[ilog2(VM_READ)]	= TASK_DIAG_VMA_F_READ,
+		[ilog2(VM_WRITE)]	= TASK_DIAG_VMA_F_WRITE,
+		[ilog2(VM_EXEC)]	= TASK_DIAG_VMA_F_EXEC,
+		[ilog2(VM_SHARED)]	= TASK_DIAG_VMA_F_SHARED,
+		[ilog2(VM_MAYREAD)]	= TASK_DIAG_VMA_F_MAYREAD,
+		[ilog2(VM_MAYWRITE)]	= TASK_DIAG_VMA_F_MAYWRITE,
+		[ilog2(VM_MAYEXEC)]	= TASK_DIAG_VMA_F_MAYEXEC,
+		[ilog2(VM_MAYSHARE)]	= TASK_DIAG_VMA_F_MAYSHARE,
+		[ilog2(VM_GROWSDOWN)]	= TASK_DIAG_VMA_F_GROWSDOWN,
+		[ilog2(VM_PFNMAP)]	= TASK_DIAG_VMA_F_PFNMAP,
+		[ilog2(VM_DENYWRITE)]	= TASK_DIAG_VMA_F_DENYWRITE,
+#ifdef CONFIG_X86_INTEL_MPX
+		[ilog2(VM_MPX)]		= TASK_DIAG_VMA_F_MPX,
+#endif
+		[ilog2(VM_LOCKED)]	= TASK_DIAG_VMA_F_LOCKED,
+		[ilog2(VM_IO)]		= TASK_DIAG_VMA_F_IO,
+		[ilog2(VM_SEQ_READ)]	= TASK_DIAG_VMA_F_SEQ_READ,
+		[ilog2(VM_RAND_READ)]	= TASK_DIAG_VMA_F_RAND_READ,
+		[ilog2(VM_DONTCOPY)]	= TASK_DIAG_VMA_F_DONTCOPY,
+		[ilog2(VM_DONTEXPAND)]	= TASK_DIAG_VMA_F_DONTEXPAND,
+		[ilog2(VM_ACCOUNT)]	= TASK_DIAG_VMA_F_ACCOUNT,
+		[ilog2(VM_NORESERVE)]	= TASK_DIAG_VMA_F_NORESERVE,
+		[ilog2(VM_HUGETLB)]	= TASK_DIAG_VMA_F_HUGETLB,
+		[ilog2(VM_ARCH_1)]	= TASK_DIAG_VMA_F_ARCH_1,
+		[ilog2(VM_DONTDUMP)]	= TASK_DIAG_VMA_F_DONTDUMP,
+#ifdef CONFIG_MEM_SOFT_DIRTY
+		[ilog2(VM_SOFTDIRTY)]	= TASK_DIAG_VMA_F_SOFTDIRTY,
+#endif
+		[ilog2(VM_MIXEDMAP)]	= TASK_DIAG_VMA_F_MIXEDMAP,
+		[ilog2(VM_HUGEPAGE)]	= TASK_DIAG_VMA_F_HUGEPAGE,
+		[ilog2(VM_NOHUGEPAGE)]	= TASK_DIAG_VMA_F_NOHUGEPAGE,
+		[ilog2(VM_MERGEABLE)]	= TASK_DIAG_VMA_F_MERGEABLE,
+	};
+	size_t i;
+
+	for (i = 0; i < BITS_PER_LONG; i++) {
+		if (vma->vm_flags & (1UL << i))
+			flags |= mnemonics[i];
+	}
+
+	return flags;
+}
+
+static int task_vma_num(struct mm_struct *mm)
+{
+	struct vm_area_struct *vma;
+	int n_vma = 0;
+
+	if (!mm || !atomic_inc_not_zero(&mm->mm_users))
+		return 0;
+
+	down_read(&mm->mmap_sem);
+	for (vma = mm->mmap; vma; vma = vma->vm_next, n_vma++)
+		;
+
+	up_read(&mm->mmap_sem);
+	mmput(mm);
+
+	return n_vma;
+}
+
+/*
+ * use a tmp variable and copy to input arg to deal with
+ * alignment issues. diag_vma contains u64 elements which
+ * means extended load operations can be used and those can
+ * require 8-byte alignment (e.g., sparc)
+ */
+static void fill_diag_vma(struct vm_area_struct *vma,
+			  struct task_diag_vma *diag_vma)
+{
+	struct task_diag_vma tmp;
+
+	/* We don't show the stack guard page in /proc/maps */
+	tmp.start = vma->vm_start;
+	if (stack_guard_page_start(vma, tmp.start))
+		tmp.start += PAGE_SIZE;
+
+	tmp.end = vma->vm_end;
+	if (stack_guard_page_end(vma, tmp.end))
+		tmp.end -= PAGE_SIZE;
+	tmp.vm_flags = get_vma_flags(vma);
+
+	if (vma->vm_file) {
+		struct inode *inode = file_inode(vma->vm_file);
+		dev_t dev;
+
+		dev = inode->i_sb->s_dev;
+		tmp.major = MAJOR(dev);
+		tmp.minor = MINOR(dev);
+		tmp.inode = inode->i_ino;
+		tmp.generation = inode->i_generation;
+		tmp.pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
+	} else {
+		tmp.major = 0;
+		tmp.minor = 0;
+		tmp.inode = 0;
+		tmp.generation = 0;
+		tmp.pgoff = 0;
+	}
+
+	memcpy(diag_vma, &tmp, sizeof(*diag_vma));
+}
+
+static const char *get_vma_name(struct vm_area_struct *vma, char *page)
+{
+	const char *name = NULL;
+
+	if (vma->vm_file) {
+		name = d_path(&vma->vm_file->f_path, page, PAGE_SIZE);
+		goto out;
+	}
+
+	if (vma->vm_ops && vma->vm_ops->name) {
+		name = vma->vm_ops->name(vma);
+		if (name)
+			goto out;
+	}
+
+	name = arch_vma_name(vma);
+
+out:
+	return name;
+}
+
+static int fill_vma(struct task_struct *p, struct sk_buff *skb,
+			struct netlink_callback *cb, bool *progress)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;
+	struct nlattr *attr = NULL;
+	struct task_diag_vma *diag_vma;
+	unsigned long mark = 0;
+	char *page;
+	int i, rc = -EMSGSIZE;
+
+	if (cb)
+		mark = cb->args[3];
+
+	mm = p->mm;
+	if (!mm || !atomic_inc_not_zero(&mm->mm_users))
+		return 0;
+
+	page = (char *)__get_free_page(GFP_TEMPORARY);
+	if (!page) {
+		mmput(mm);
+		return -ENOMEM;
+	}
+
+	down_read(&mm->mmap_sem);
+	for (vma = mm->mmap; vma; vma = vma->vm_next, i++) {
+		unsigned char *b = skb_tail_pointer(skb);
+		const char *name;
+		void *pfile;
+
+
+		if (mark >= vma->vm_start)
+			continue;
+
+		/* setup pointer for next map */
+		if (attr == NULL) {
+			attr = nla_reserve(skb, TASK_DIAG_VMA, sizeof(*diag_vma));
+			if (!attr)
+				goto err;
+
+			diag_vma = nla_data(attr);
+		} else {
+			diag_vma = nla_reserve_nohdr(skb, sizeof(*diag_vma));
+
+			if (diag_vma == NULL) {
+				nlmsg_trim(skb, b);
+				goto out;
+			}
+		}
+
+		fill_diag_vma(vma, diag_vma);
+
+		name = get_vma_name(vma, page);
+		if (IS_ERR(name)) {
+			nlmsg_trim(skb, b);
+			rc = PTR_ERR(name);
+			goto out;
+		}
+
+		if (name) {
+			diag_vma->name_len = strlen(name) + 1;
+
+			/* reserves NLA_ALIGN(len) */
+			pfile = nla_reserve_nohdr(skb, diag_vma->name_len);
+			if (pfile == NULL) {
+				nlmsg_trim(skb, b);
+				goto out;
+			}
+			diag_vma->name_off = pfile - (void *) diag_vma;
+			memcpy(pfile, name, diag_vma->name_len);
+		} else {
+			diag_vma->name_len = 0;
+			diag_vma->name_off = 0;
+		}
+
+		mark = vma->vm_start;
+
+		diag_vma->vma_len = skb_tail_pointer(skb) - (unsigned char *) diag_vma;
+
+		*progress = true;
+	}
+
+	rc = 0;
+	mark = 0;
+out:
+	if (*progress)
+		attr->nla_len = skb_tail_pointer(skb) - (unsigned char *) attr;
+
+err:
+	up_read(&mm->mmap_sem);
+	mmput(mm);
+	free_page((unsigned long) page);
+	if (cb)
+		cb->args[3] = mark;
+
+	return rc;
+}
+
 static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
 				u64 show_flags, u32 portid, u32 seq,
 				struct netlink_callback *cb)
 {
 	void *reply;
 	int err = 0, i = 0, n = 0;
+	bool progress = false;
 	int flags = 0;
 	u32 pid;
 
@@ -198,13 +439,21 @@ static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
 		i++;
 	}
 
+	if (show_flags & TASK_DIAG_SHOW_VMA) {
+		if (i >= n)
+			err = fill_vma(tsk, skb, cb, &progress);
+		if (err)
+			goto err;
+		i++;
+	}
+
 	genlmsg_end(skb, reply);
 	if (cb)
 		cb->args[2] = 0;
 
 	return 0;
 err:
-	if (err == -EMSGSIZE && i != 0) {
+	if (err == -EMSGSIZE && (i > n || progress)) {
 		if (cb)
 			cb->args[2] = i;
 		genlmsg_end(skb, reply);
@@ -374,7 +623,7 @@ int taskdiag_doit(struct sk_buff *skb, struct genl_info *info)
 		return -EPERM;
 	}
 
-	size = taskdiag_packet_size(req.show_flags);
+	size = taskdiag_packet_size(req.show_flags, task_vma_num(tsk->mm));
 
 	while (1) {
 		msg = genlmsg_new(size, GFP_KERNEL);
-- 
2.1.0

^ permalink raw reply related

* [PATCH 13/24] task_diag: shows memory consumption for memory mappings (v2)
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-api, Andrey Vagin, Oleg Nesterov, Andrew Morton,
	Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi, Arnd Bergmann,
	Arnaldo Carvalho de Melo, David Ahern, Andy Lutomirski,
	Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin@openvz.org>

The same information are shown in /proc/self/smaps.

v2: account the task_diag_vma_stat struct in taskdiag_packet_size()
    Fix 8-byte alignment for vma and vma_stats // David Ahern

Cc: David Ahern <dsahern@gmail.com>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
 fs/proc/task_mmu.c             | 14 +--------
 include/linux/proc_fs.h        | 17 +++++++++++
 include/uapi/linux/task_diag.h | 25 +++++++++++++++++
 kernel/taskdiag.c              | 64 ++++++++++++++++++++++++++++++++++++++----
 4 files changed, 101 insertions(+), 19 deletions(-)

diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 6dee68d..c89f68c 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -435,18 +435,6 @@ const struct file_operations proc_tid_maps_operations = {
 #define PSS_SHIFT 12
 
 #ifdef CONFIG_PROC_PAGE_MONITOR
-struct mem_size_stats {
-	unsigned long resident;
-	unsigned long shared_clean;
-	unsigned long shared_dirty;
-	unsigned long private_clean;
-	unsigned long private_dirty;
-	unsigned long referenced;
-	unsigned long anonymous;
-	unsigned long anonymous_thp;
-	unsigned long swap;
-	u64 pss;
-};
 
 static void smaps_account(struct mem_size_stats *mss, struct page *page,
 		unsigned long size, bool young, bool dirty)
@@ -526,7 +514,7 @@ static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
 }
 #endif
 
-static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
+int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 			   struct mm_walk *walk)
 {
 	struct vm_area_struct *vma = walk->vma;
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 497e58c..62d0079 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -92,4 +92,21 @@ struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter);
 struct task_struct *
 task_next_child(struct task_struct *parent, struct task_struct *prev, unsigned int pos);
 
+struct mem_size_stats {
+	unsigned long resident;
+	unsigned long shared_clean;
+	unsigned long shared_dirty;
+	unsigned long private_clean;
+	unsigned long private_dirty;
+	unsigned long referenced;
+	unsigned long anonymous;
+	unsigned long anonymous_thp;
+	unsigned long swap;
+	u64 pss;
+};
+
+struct mm_walk;
+int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
+			   struct mm_walk *walk);
+
 #endif /* _LINUX_PROC_FS_H */
diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
index 943d8d1..73d33c8 100644
--- a/include/uapi/linux/task_diag.h
+++ b/include/uapi/linux/task_diag.h
@@ -11,6 +11,7 @@ enum {
 	TASK_DIAG_CRED,
 	TASK_DIAG_STAT,
 	TASK_DIAG_VMA,
+	TASK_DIAG_VMA_STAT,
 
 	/* other attributes */
 	TASK_DIAG_PID	= 64,	/* u32 */
@@ -23,6 +24,7 @@ enum {
 #define TASK_DIAG_SHOW_CRED	(1ULL << TASK_DIAG_CRED)
 #define TASK_DIAG_SHOW_STAT	(1ULL << TASK_DIAG_STAT)
 #define TASK_DIAG_SHOW_VMA	(1ULL << TASK_DIAG_VMA)
+#define TASK_DIAG_SHOW_VMA_STAT	(1ULL << TASK_DIAG_VMA_STAT)
 
 enum {
 	TASK_DIAG_RUNNING,
@@ -96,6 +98,19 @@ struct task_diag_creds {
 #define TASK_DIAG_VMA_F_NOHUGEPAGE	(1ULL << 26)
 #define TASK_DIAG_VMA_F_MERGEABLE	(1ULL << 27)
 
+struct task_diag_vma_stat {
+	__u64 resident;
+	__u64 shared_clean;
+	__u64 shared_dirty;
+	__u64 private_clean;
+	__u64 private_dirty;
+	__u64 referenced;
+	__u64 anonymous;
+	__u64 anonymous_thp;
+	__u64 swap;
+	__u64 pss;
+} __attribute__((__aligned__(NLA_ALIGNTO)));
+
 /* task_diag_vma must be NLA_ALIGN'ed */
 struct task_diag_vma {
 	__u64 start, end;
@@ -108,6 +123,8 @@ struct task_diag_vma {
 	__u16 vma_len;
 	__u16 name_off;
 	__u16 name_len;
+	__u16 stat_off;
+	__u16 stat_len;
 } __attribute__((__aligned__(NLA_ALIGNTO)));
 
 static inline char *task_diag_vma_name(struct task_diag_vma *vma)
@@ -118,6 +135,14 @@ static inline char *task_diag_vma_name(struct task_diag_vma *vma)
 	return ((char *)vma) + vma->name_off;
 }
 
+static inline struct task_diag_vma_stat *task_diag_vma_stat(struct task_diag_vma *vma)
+{
+	if (!vma->stat_len)
+		return NULL;
+
+	return ((void *)vma) + vma->stat_off;
+}
+
 #define TASK_DIAG_DUMP_ALL	0
 #define TASK_DIAG_DUMP_CHILDREN	1
 
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index c488c1b..8e00c3e 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c
@@ -24,11 +24,17 @@ static size_t taskdiag_packet_size(u64 show_flags, int n_vma)
 		size += nla_total_size(sizeof(struct taskstats));
 
 	if (show_flags & TASK_DIAG_SHOW_VMA && n_vma > 0) {
+		size_t entry_size;
+
 		/*
 		 * 128 is a schwag on average path length for maps; used to
 		 * ballpark initial memory allocation for genl msg
 		 */
-		size += nla_total_size(sizeof(struct task_diag_vma) * n_vma + 128);
+		entry_size = sizeof(struct task_diag_vma) + 128;
+
+		if (show_flags & TASK_DIAG_SHOW_VMA_STAT)
+			entry_size += sizeof(struct task_diag_vma_stat);
+		size += nla_total_size(entry_size * n_vma);
 	}
 
 	return size;
@@ -292,8 +298,37 @@ out:
 	return name;
 }
 
+static void fill_diag_vma_stat(struct vm_area_struct *vma, struct task_diag_vma_stat *stat)
+{
+	struct task_diag_vma_stat tmp;
+	struct mem_size_stats mss;
+	struct mm_walk smaps_walk = {
+		.pmd_entry = smaps_pte_range,
+		.mm = vma->vm_mm,
+		.private = &mss,
+	};
+
+	memset(&mss, 0, sizeof mss);
+	memset(&tmp, 0, sizeof(tmp));
+
+	/* mmap_sem is held in m_start */
+	walk_page_vma(vma, &smaps_walk);
+
+	tmp.resident		= mss.resident;
+	tmp.pss			= mss.pss;
+	tmp.shared_clean	= mss.shared_clean;
+	tmp.private_clean	= mss.private_clean;
+	tmp.private_dirty	= mss.private_dirty;
+	tmp.referenced		= mss.referenced;
+	tmp.anonymous		= mss.anonymous;
+	tmp.anonymous_thp	= mss.anonymous_thp;
+	tmp.swap		= mss.swap;
+
+	memcpy(stat, &tmp, sizeof(*stat));
+}
+
 static int fill_vma(struct task_struct *p, struct sk_buff *skb,
-			struct netlink_callback *cb, bool *progress)
+		    struct netlink_callback *cb, bool *progress, u64 show_flags)
 {
 	struct vm_area_struct *vma;
 	struct mm_struct *mm;
@@ -301,7 +336,7 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb,
 	struct task_diag_vma *diag_vma;
 	unsigned long mark = 0;
 	char *page;
-	int i, rc = -EMSGSIZE;
+	int i, rc = -EMSGSIZE, size;
 
 	if (cb)
 		mark = cb->args[3];
@@ -316,6 +351,10 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb,
 		return -ENOMEM;
 	}
 
+	size = NLA_ALIGN(sizeof(struct task_diag_vma));
+	if (show_flags & TASK_DIAG_SHOW_VMA_STAT)
+		size += NLA_ALIGN(sizeof(struct task_diag_vma_stat));
+
 	down_read(&mm->mmap_sem);
 	for (vma = mm->mmap; vma; vma = vma->vm_next, i++) {
 		unsigned char *b = skb_tail_pointer(skb);
@@ -328,13 +367,13 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb,
 
 		/* setup pointer for next map */
 		if (attr == NULL) {
-			attr = nla_reserve(skb, TASK_DIAG_VMA, sizeof(*diag_vma));
+			attr = nla_reserve(skb, TASK_DIAG_VMA, size);
 			if (!attr)
 				goto err;
 
 			diag_vma = nla_data(attr);
 		} else {
-			diag_vma = nla_reserve_nohdr(skb, sizeof(*diag_vma));
+			diag_vma = nla_reserve_nohdr(skb, size);
 
 			if (diag_vma == NULL) {
 				nlmsg_trim(skb, b);
@@ -344,6 +383,19 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb,
 
 		fill_diag_vma(vma, diag_vma);
 
+		if (show_flags & TASK_DIAG_SHOW_VMA_STAT) {
+			struct task_diag_vma_stat *stat;
+
+			stat = (void *) diag_vma + NLA_ALIGN(sizeof(struct task_diag_vma));
+
+			fill_diag_vma_stat(vma, stat);
+			diag_vma->stat_len = sizeof(struct task_diag_vma_stat);
+			diag_vma->stat_off = (void *) stat - (void *)diag_vma;
+		} else {
+			diag_vma->stat_len = 0;
+			diag_vma->stat_off = 0;
+		}
+
 		name = get_vma_name(vma, page);
 		if (IS_ERR(name)) {
 			nlmsg_trim(skb, b);
@@ -441,7 +493,7 @@ static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb,
 
 	if (show_flags & TASK_DIAG_SHOW_VMA) {
 		if (i >= n)
-			err = fill_vma(tsk, skb, cb, &progress);
+			err = fill_vma(tsk, skb, cb, &progress, show_flags);
 		if (err)
 			goto err;
 		i++;
-- 
2.1.0

^ permalink raw reply related

* [PATCH 14/24] task_diag: add a marcos to enumirate memory mappings
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-api, Andrey Vagin, Oleg Nesterov, Andrew Morton,
	Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi, Arnd Bergmann,
	Arnaldo Carvalho de Melo, David Ahern, Andy Lutomirski,
	Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin@openvz.org>

This macros will help users to use this interface.

v2: Fix task_diag_for_each_vma to work with libnl // David Ahern

Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
 include/uapi/linux/task_diag.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
index 73d33c8..9e4c3c4 100644
--- a/include/uapi/linux/task_diag.h
+++ b/include/uapi/linux/task_diag.h
@@ -143,6 +143,11 @@ static inline struct task_diag_vma_stat *task_diag_vma_stat(struct task_diag_vma
 	return ((void *)vma) + vma->stat_off;
 }
 
+#define task_diag_for_each_vma(vma, attr)			\
+	for (vma = nla_data(attr);				\
+		(void *) vma < nla_data(attr) + nla_len(attr);	\
+		vma = (void *) vma + vma->vma_len)
+
 #define TASK_DIAG_DUMP_ALL	0
 #define TASK_DIAG_DUMP_CHILDREN	1
 
-- 
2.1.0

^ permalink raw reply related

* [PATCH 15/24] proc: give task_struct instead of pid into first_tid
From: Andrey Vagin @ 2015-07-06  8:47 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-api, Andrey Vagin, Oleg Nesterov, Andrew Morton,
	Cyrill Gorcunov, Pavel Emelyanov, Roger Luethi, Arnd Bergmann,
	Arnaldo Carvalho de Melo, David Ahern, Andy Lutomirski,
	Pavel Odintsov
In-Reply-To: <1436172445-6979-1-git-send-email-avagin@openvz.org>

It will be more convenient when this function will be used in
task_diag.

Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
 fs/proc/base.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 88cf6ae..af797d9 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -3225,19 +3225,17 @@ out_no_task:
  * In the case of a seek we start with the leader and walk nr
  * threads past it.
  */
-static struct task_struct *first_tid(struct pid *pid, int tid, loff_t f_pos,
+static struct task_struct *
+first_tid(struct task_struct *task, int tid, loff_t f_pos,
 					struct pid_namespace *ns)
 {
-	struct task_struct *pos, *task;
+	struct task_struct *pos;
 	unsigned long nr = f_pos;
 
 	if (nr != f_pos)	/* 32bit overflow? */
 		return NULL;
 
 	rcu_read_lock();
-	task = pid_task(pid, PIDTYPE_PID);
-	if (!task)
-		goto fail;
 
 	/* Attempt to start with the tid of a thread */
 	if (tid && nr) {
@@ -3294,7 +3292,7 @@ static struct task_struct *next_tid(struct task_struct *start)
 static int proc_task_readdir(struct file *file, struct dir_context *ctx)
 {
 	struct inode *inode = file_inode(file);
-	struct task_struct *task;
+	struct task_struct *start, *task;
 	struct pid_namespace *ns;
 	int tid;
 
@@ -3304,13 +3302,17 @@ static int proc_task_readdir(struct file *file, struct dir_context *ctx)
 	if (!dir_emit_dots(file, ctx))
 		return 0;
 
+	start = get_proc_task(inode);
+	if (!start)
+		return 0;
+
 	/* f_version caches the tgid value that the last readdir call couldn't
 	 * return. lseek aka telldir automagically resets f_version to 0.
 	 */
 	ns = inode->i_sb->s_fs_info;
 	tid = (int)file->f_version;
 	file->f_version = 0;
-	for (task = first_tid(proc_pid(inode), tid, ctx->pos - 2, ns);
+	for (task = first_tid(start, tid, ctx->pos - 2, ns);
 	     task;
 	     task = next_tid(task), ctx->pos++) {
 		char name[PROC_NUMBUF];
@@ -3327,6 +3329,8 @@ static int proc_task_readdir(struct file *file, struct dir_context *ctx)
 		}
 	}
 
+	put_task_struct(start);
+
 	return 0;
 }
 
-- 
2.1.0

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox