#!/usr/local/bin/stap /* * This script is used to fail __alloc_pages() function in mm/page_alloc.c */ probe begin(0) { fij_load_param(20000,0,100,0,0,1,1000); fij_add_option("min_order",0,"Minimum order(base 2) of the allocation") fij_add_gfp_wait_param() fij_add_gfp_highmem_param() } global min_order probe begin(2000) { min_order = fij_params["min_order"] } function should_fail_alloc(order:long,flags:long) { if (order < min_order) { fij_logger(100,sprintf("Skipping on order %d",order)) return 0 } else fij_logger(100,sprintf("Continuing on order %d",order)) if (fij_should_fail_gfp_wait(flags) == 0) return 0 if (fij_should_fail_gfp_highmem(flags) == 0) return 0 if (fij_should_fail() == 0) return 0 return 1 } global zones_save,under_fail /* * zones_save : Temporary variable to save zone information that will be * assigned fake value to induce fault. * under_fail : Flag to denote whether system is under fault injected condition. * Both these variables are 'per-cpu' variables to prevent race conditions. */ function nullify(zptr:long,cpu:long) %{ struct zones **z = (struct zones **)(THIS->zptr); THIS->__retvalue = (long)*z; *z = NULL; %} /* * Method of fault injection : * The function __alloc_pages() is failed by using an if condition on * variable z (=zonelist->zones). * To fail, the value of *(zonelist->zones) is saved in zones_save and * set to NULL to cause the function to return NULL value. * Once the fault is injected, the value of zonelist->zones is restored. */ probe kernel.function("__alloc_pages@mm/page_alloc.c") { if (should_fail_alloc($order,$gfp_mask) == 0) next zones_save[cpu()] = nullify($zonelist->zones,cpu()) under_fail[cpu()] = 1 fij_done_fail() } function restore(zones:long,zones_save:long) %{ struct zones **z = (struct zones **)(THIS->zones); *z = (struct zones *)THIS->zones_save; %} probe kernel.function("__alloc_pages@mm/page_alloc.c").return { if (under_fail[cpu()]) { restore($zonelist->zones,zones_save[cpu()]) under_fail[cpu()] = 0 } }