Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

zram: modernize writeback interface

The writeback interface supports a page_index=N parameter which performs
writeback of the given page. Since we rarely need to writeback just one
single page, the typical use case involves a number of writeback calls,
each performing writeback of one page:

echo page_index=100 > zram0/writeback
...
echo page_index=200 > zram0/writeback
echo page_index=500 > zram0/writeback
...
echo page_index=700 > zram0/writeback

One obvious downside of this is that it increases the number of syscalls.
Less obvious, but a significantly more important downside, is that when
given only one page to post-process zram cannot perform an optimal target
selection. This becomes a critical limitation when writeback_limit is
enabled, because under writeback_limit we want to guarantee the highest
memory savings hence we first need to writeback pages that release the
highest amount of zsmalloc pool memory.

This patch adds page_indexes=LOW-HIGH parameter to the writeback
interface:

echo page_indexes=100-200 page_indexes=500-700 > zram0/writeback

This gives zram a chance to apply an optimal target selection strategy on
each iteration of the writeback loop.

We also now permit multiple page_index parameters per call (previously
zram would recognize only one page_index) and a mix or single pages and
page ranges:

echo page_index=42 page_index=99 page_indexes=100-200 \
page_indexes=500-700 > zram0/writeback

Apart from that the patch also unifies parameters passing and resembles
other "modern" zram device attributes (e.g. recompression), while the old
interface used a mixed scheme: values-less parameters for mode and a
key=value format for page_index. We still support the "old" value-less
format for compatibility reasons.

[senozhatsky@chromium.org: simplify parse_page_index() range checks, per Brian]
nk: https://lkml.kernel.org/r/20250404015327.2427684-1-senozhatsky@chromium.org
[sozhatsky@chromium.org: fix uninitialized variable in zram_writeback_slots(), per Dan]
nk: https://lkml.kernel.org/r/20250409112611.1154282-1-senozhatsky@chromium.org
Link: https://lkml.kernel.org/r/20250327015818.4148660-1-senozhatsky@chromium.org
Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Reviewed-by: Brian Geffon <bgeffon@google.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Richard Chang <richardycc@google.com>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: Dan Carpenter <dan.carpenter@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Sergey Senozhatsky and committed by
Andrew Morton
cf42d4cc 0bf19a35

+233 -106
+17
Documentation/admin-guide/blockdev/zram.rst
··· 369 369 370 370 echo "page_index=1251" > /sys/block/zramX/writeback 371 371 372 + In Linux 6.16 this interface underwent some rework. First, the interface 373 + now supports `key=value` format for all of its parameters (`type=huge_idle`, 374 + etc.) Second, the support for `page_indexes` was introduced, which specify 375 + `LOW-HIGH` range (or ranges) of pages to be written-back. This reduces the 376 + number of syscalls, but more importantly this enables optimal post-processing 377 + target selection strategy. Usage example:: 378 + 379 + echo "type=idle" > /sys/block/zramX/writeback 380 + echo "page_indexes=1-100 page_indexes=200-300" > \ 381 + /sys/block/zramX/writeback 382 + 383 + We also now permit multiple page_index params per call and a mix of 384 + single pages and page ranges:: 385 + 386 + echo page_index=42 page_index=99 page_indexes=100-200 \ 387 + page_indexes=500-700 > /sys/block/zramX/writeback 388 + 372 389 If there are lots of write IO with flash device, potentially, it has 373 390 flash wearout problem so that admin needs to design write limitation 374 391 to guarantee storage health for entire product life.
+216 -106
drivers/block/zram/zram_drv.c
··· 734 734 submit_bio(bio); 735 735 } 736 736 737 - #define PAGE_WB_SIG "page_index=" 738 - 739 - #define PAGE_WRITEBACK 0 740 - #define HUGE_WRITEBACK (1<<0) 741 - #define IDLE_WRITEBACK (1<<1) 742 - #define INCOMPRESSIBLE_WRITEBACK (1<<2) 743 - 744 - static int scan_slots_for_writeback(struct zram *zram, u32 mode, 745 - unsigned long nr_pages, 746 - unsigned long index, 747 - struct zram_pp_ctl *ctl) 737 + static int zram_writeback_slots(struct zram *zram, struct zram_pp_ctl *ctl) 748 738 { 749 - for (; nr_pages != 0; index++, nr_pages--) { 750 - bool ok = true; 751 - 752 - zram_slot_lock(zram, index); 753 - if (!zram_allocated(zram, index)) 754 - goto next; 755 - 756 - if (zram_test_flag(zram, index, ZRAM_WB) || 757 - zram_test_flag(zram, index, ZRAM_SAME)) 758 - goto next; 759 - 760 - if (mode & IDLE_WRITEBACK && 761 - !zram_test_flag(zram, index, ZRAM_IDLE)) 762 - goto next; 763 - if (mode & HUGE_WRITEBACK && 764 - !zram_test_flag(zram, index, ZRAM_HUGE)) 765 - goto next; 766 - if (mode & INCOMPRESSIBLE_WRITEBACK && 767 - !zram_test_flag(zram, index, ZRAM_INCOMPRESSIBLE)) 768 - goto next; 769 - 770 - ok = place_pp_slot(zram, ctl, index); 771 - next: 772 - zram_slot_unlock(zram, index); 773 - if (!ok) 774 - break; 775 - } 776 - 777 - return 0; 778 - } 779 - 780 - static ssize_t writeback_store(struct device *dev, 781 - struct device_attribute *attr, const char *buf, size_t len) 782 - { 783 - struct zram *zram = dev_to_zram(dev); 784 - unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; 785 - struct zram_pp_ctl *ctl = NULL; 786 - struct zram_pp_slot *pps; 787 - unsigned long index = 0; 788 - struct bio bio; 789 - struct bio_vec bio_vec; 790 - struct page *page = NULL; 791 - ssize_t ret = len; 792 - int mode, err; 793 739 unsigned long blk_idx = 0; 794 - 795 - if (sysfs_streq(buf, "idle")) 796 - mode = IDLE_WRITEBACK; 797 - else if (sysfs_streq(buf, "huge")) 798 - mode = HUGE_WRITEBACK; 799 - else if (sysfs_streq(buf, "huge_idle")) 800 - mode = IDLE_WRITEBACK | HUGE_WRITEBACK; 801 - else if (sysfs_streq(buf, "incompressible")) 802 - mode = INCOMPRESSIBLE_WRITEBACK; 803 - else { 804 - if (strncmp(buf, PAGE_WB_SIG, sizeof(PAGE_WB_SIG) - 1)) 805 - return -EINVAL; 806 - 807 - if (kstrtol(buf + sizeof(PAGE_WB_SIG) - 1, 10, &index) || 808 - index >= nr_pages) 809 - return -EINVAL; 810 - 811 - nr_pages = 1; 812 - mode = PAGE_WRITEBACK; 813 - } 814 - 815 - down_read(&zram->init_lock); 816 - if (!init_done(zram)) { 817 - ret = -EINVAL; 818 - goto release_init_lock; 819 - } 820 - 821 - /* Do not permit concurrent post-processing actions. */ 822 - if (atomic_xchg(&zram->pp_in_progress, 1)) { 823 - up_read(&zram->init_lock); 824 - return -EAGAIN; 825 - } 826 - 827 - if (!zram->backing_dev) { 828 - ret = -ENODEV; 829 - goto release_init_lock; 830 - } 740 + struct page *page = NULL; 741 + struct zram_pp_slot *pps; 742 + struct bio_vec bio_vec; 743 + struct bio bio; 744 + int ret = 0, err; 745 + u32 index; 831 746 832 747 page = alloc_page(GFP_KERNEL); 833 - if (!page) { 834 - ret = -ENOMEM; 835 - goto release_init_lock; 836 - } 837 - 838 - ctl = init_pp_ctl(); 839 - if (!ctl) { 840 - ret = -ENOMEM; 841 - goto release_init_lock; 842 - } 843 - 844 - scan_slots_for_writeback(zram, mode, nr_pages, index, ctl); 748 + if (!page) 749 + return -ENOMEM; 845 750 846 751 while ((pps = select_pp_slot(ctl))) { 847 752 spin_lock(&zram->wb_limit_lock); ··· 834 929 835 930 if (blk_idx) 836 931 free_block_bdev(zram, blk_idx); 837 - 838 - release_init_lock: 839 932 if (page) 840 933 __free_page(page); 934 + 935 + return ret; 936 + } 937 + 938 + #define PAGE_WRITEBACK 0 939 + #define HUGE_WRITEBACK (1 << 0) 940 + #define IDLE_WRITEBACK (1 << 1) 941 + #define INCOMPRESSIBLE_WRITEBACK (1 << 2) 942 + 943 + static int parse_page_index(char *val, unsigned long nr_pages, 944 + unsigned long *lo, unsigned long *hi) 945 + { 946 + int ret; 947 + 948 + ret = kstrtoul(val, 10, lo); 949 + if (ret) 950 + return ret; 951 + if (*lo >= nr_pages) 952 + return -ERANGE; 953 + *hi = *lo + 1; 954 + return 0; 955 + } 956 + 957 + static int parse_page_indexes(char *val, unsigned long nr_pages, 958 + unsigned long *lo, unsigned long *hi) 959 + { 960 + char *delim; 961 + int ret; 962 + 963 + delim = strchr(val, '-'); 964 + if (!delim) 965 + return -EINVAL; 966 + 967 + *delim = 0x00; 968 + ret = kstrtoul(val, 10, lo); 969 + if (ret) 970 + return ret; 971 + if (*lo >= nr_pages) 972 + return -ERANGE; 973 + 974 + ret = kstrtoul(delim + 1, 10, hi); 975 + if (ret) 976 + return ret; 977 + if (*hi >= nr_pages || *lo > *hi) 978 + return -ERANGE; 979 + *hi += 1; 980 + return 0; 981 + } 982 + 983 + static int parse_mode(char *val, u32 *mode) 984 + { 985 + *mode = 0; 986 + 987 + if (!strcmp(val, "idle")) 988 + *mode = IDLE_WRITEBACK; 989 + if (!strcmp(val, "huge")) 990 + *mode = HUGE_WRITEBACK; 991 + if (!strcmp(val, "huge_idle")) 992 + *mode = IDLE_WRITEBACK | HUGE_WRITEBACK; 993 + if (!strcmp(val, "incompressible")) 994 + *mode = INCOMPRESSIBLE_WRITEBACK; 995 + 996 + if (*mode == 0) 997 + return -EINVAL; 998 + return 0; 999 + } 1000 + 1001 + static int scan_slots_for_writeback(struct zram *zram, u32 mode, 1002 + unsigned long lo, unsigned long hi, 1003 + struct zram_pp_ctl *ctl) 1004 + { 1005 + u32 index = lo; 1006 + 1007 + while (index < hi) { 1008 + bool ok = true; 1009 + 1010 + zram_slot_lock(zram, index); 1011 + if (!zram_allocated(zram, index)) 1012 + goto next; 1013 + 1014 + if (zram_test_flag(zram, index, ZRAM_WB) || 1015 + zram_test_flag(zram, index, ZRAM_SAME)) 1016 + goto next; 1017 + 1018 + if (mode & IDLE_WRITEBACK && 1019 + !zram_test_flag(zram, index, ZRAM_IDLE)) 1020 + goto next; 1021 + if (mode & HUGE_WRITEBACK && 1022 + !zram_test_flag(zram, index, ZRAM_HUGE)) 1023 + goto next; 1024 + if (mode & INCOMPRESSIBLE_WRITEBACK && 1025 + !zram_test_flag(zram, index, ZRAM_INCOMPRESSIBLE)) 1026 + goto next; 1027 + 1028 + ok = place_pp_slot(zram, ctl, index); 1029 + next: 1030 + zram_slot_unlock(zram, index); 1031 + if (!ok) 1032 + break; 1033 + index++; 1034 + } 1035 + 1036 + return 0; 1037 + } 1038 + 1039 + static ssize_t writeback_store(struct device *dev, 1040 + struct device_attribute *attr, 1041 + const char *buf, size_t len) 1042 + { 1043 + struct zram *zram = dev_to_zram(dev); 1044 + u64 nr_pages = zram->disksize >> PAGE_SHIFT; 1045 + unsigned long lo = 0, hi = nr_pages; 1046 + struct zram_pp_ctl *ctl = NULL; 1047 + char *args, *param, *val; 1048 + ssize_t ret = len; 1049 + int err, mode = 0; 1050 + 1051 + down_read(&zram->init_lock); 1052 + if (!init_done(zram)) { 1053 + up_read(&zram->init_lock); 1054 + return -EINVAL; 1055 + } 1056 + 1057 + /* Do not permit concurrent post-processing actions. */ 1058 + if (atomic_xchg(&zram->pp_in_progress, 1)) { 1059 + up_read(&zram->init_lock); 1060 + return -EAGAIN; 1061 + } 1062 + 1063 + if (!zram->backing_dev) { 1064 + ret = -ENODEV; 1065 + goto release_init_lock; 1066 + } 1067 + 1068 + ctl = init_pp_ctl(); 1069 + if (!ctl) { 1070 + ret = -ENOMEM; 1071 + goto release_init_lock; 1072 + } 1073 + 1074 + args = skip_spaces(buf); 1075 + while (*args) { 1076 + args = next_arg(args, &param, &val); 1077 + 1078 + /* 1079 + * Workaround to support the old writeback interface. 1080 + * 1081 + * The old writeback interface has a minor inconsistency and 1082 + * requires key=value only for page_index parameter, while the 1083 + * writeback mode is a valueless parameter. 1084 + * 1085 + * This is not the case anymore and now all parameters are 1086 + * required to have values, however, we need to support the 1087 + * legacy writeback interface format so we check if we can 1088 + * recognize a valueless parameter as the (legacy) writeback 1089 + * mode. 1090 + */ 1091 + if (!val || !*val) { 1092 + err = parse_mode(param, &mode); 1093 + if (err) { 1094 + ret = err; 1095 + goto release_init_lock; 1096 + } 1097 + 1098 + scan_slots_for_writeback(zram, mode, lo, hi, ctl); 1099 + break; 1100 + } 1101 + 1102 + if (!strcmp(param, "type")) { 1103 + err = parse_mode(val, &mode); 1104 + if (err) { 1105 + ret = err; 1106 + goto release_init_lock; 1107 + } 1108 + 1109 + scan_slots_for_writeback(zram, mode, lo, hi, ctl); 1110 + break; 1111 + } 1112 + 1113 + if (!strcmp(param, "page_index")) { 1114 + err = parse_page_index(val, nr_pages, &lo, &hi); 1115 + if (err) { 1116 + ret = err; 1117 + goto release_init_lock; 1118 + } 1119 + 1120 + scan_slots_for_writeback(zram, mode, lo, hi, ctl); 1121 + continue; 1122 + } 1123 + 1124 + if (!strcmp(param, "page_indexes")) { 1125 + err = parse_page_indexes(val, nr_pages, &lo, &hi); 1126 + if (err) { 1127 + ret = err; 1128 + goto release_init_lock; 1129 + } 1130 + 1131 + scan_slots_for_writeback(zram, mode, lo, hi, ctl); 1132 + continue; 1133 + } 1134 + } 1135 + 1136 + err = zram_writeback_slots(zram, ctl); 1137 + if (err) 1138 + ret = err; 1139 + 1140 + release_init_lock: 841 1141 release_pp_ctl(zram, ctl); 842 1142 atomic_set(&zram->pp_in_progress, 0); 843 1143 up_read(&zram->init_lock);