Re: [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags

Dmitry Adamushko
2012-02-12T14:08:30Z

On 10 February 2012 01:16, John Stultz <john.stultz@linaro.org> wrote:

[ ... ]

> +/*
> + * Mark a region as nonvolatile, returns 1 if any pages in the region
> + * were purged.
> + */
> +long mapping_range_nonvolatile(struct address_space *mapping,
> +                               pgoff_t start_index, pgoff_t end_index)
> +{
> +       struct volatile_range *new;
> +       struct range_tree_node *node;
> +       int ret  = 0;
> +       u64 start, end;
> +       start = (u64)start_index;
> +       end = (u64)end_index;
> +
> +       mutex_lock(&mapping->vlist_mutex);
> +       node = range_tree_in_range(mapping->volatile_root, start, end);
> +       while (node) {
> +               struct volatile_range *vrange;
> +               vrange = container_of(node, struct volatile_range, range_node);
> +
> +               ret |= vrange->purged;

again, racing with volatile_shrink() here, so we can return a stale state.

> +
> +               if (start <= node->start && end >= node->end) {
> +                       vrange_del(vrange);
> +               } else if (node->start >= start) {
> +                       volatile_range_shrink(vrange, end+1, node->end);
> +               } else if (node->end <= end) {
> +                       volatile_range_shrink(vrange, node->start, start-1);
> +               } else {
> +                       /* create new node */
> +                       new = vrange_alloc(); /* XXX ENOMEM HERE? */
> +
> +                       new->mapping = mapping;
> +                       new->range_node.start = end + 1;
> +                       new->range_node.end = node->end;

new->purged = vrange->purged ?

> +                       volatile_range_shrink(vrange, node->start, start-1);
> +                       mapping->volatile_root =
> +                               range_tree_add(mapping->volatile_root,
> +                                               &new->range_node);
> +                       if (range_on_lru(new))
> +                               lru_add(new);
> +                       break;
> +               }
> +               node = range_tree_in_range(mapping->volatile_root, start, end);
> +       }
> +       mutex_unlock(&mapping->vlist_mutex);
> +
> +       return ret;
> +}
> +

Also, I have a question about mapping_range_volatile().

+long mapping_range_volatile(struct address_space *mapping,
+ pgoff_t start_index, pgoff_t end_index)
+{
+ struct volatile_range *new;
+ struct range_tree_node *node;
+
+ u64 start, end;
+ int purged = 0;
+ start = (u64)start_index;
+ end = (u64)end_index;
+
+ new = vrange_alloc();
+ if (!new)
+ return -ENOMEM;
+
+ mutex_lock(&mapping->vlist_mutex);
+
+ node = range_tree_in_range_adjacent(mapping->volatile_root, start, end);
+ while (node) {
+ struct volatile_range *vrange;
+
+ /* Already entirely marked volatile, so we're done */
+ if (node->start < start && node->end > end) {
+ /* don't need the allocated value */
+ kfree(new);
+ return 0;
+ }
+
+ /* Grab containing volatile range */
+ vrange = container_of(node, struct volatile_range, range_node);
+
+ /* resize range */
+ start = min_t(u64, start, node->start);
+ end = max_t(u64, end, node->end);
+ purged |= vrange->purged;
+
+ vrange_del(vrange);
+
+ /* get the next possible overlap */
+ node = range_tree_in_range(mapping->volatile_root, start, end);
+ }
+
+ new->mapping = mapping;
+ new->range_node.start = start;
+ new->range_node.end = end;
+ new->purged = purged;

I'm wondering whether this 'inheritance' is always desirable.

Say,

mapping_range_volatile(mapping, X, X + 1);
...
time goes by and volatile_shrink() has been called for this region.

now, a user does the following (is it considered bad user-behavior?)

mapping_range_volatile(mapping, Y = X - big_value, Z = X + big_value);

This new range will 'inherit' purged=1 from the old one and won't be
on the lru_list. Yet, it's much bigger than the old one and so many
pages are not really 'volatile'.

-- Dmitry