Thursday 14 June 2018

Linux native multipath internals

Linux native multipath provides multipathing capability in Linux. It gives the benefits of increased throughput and path failover.

Many details for it has been given in https://www.kernel.org/doc/ols/2005/ols2005v1-pages-155-176.pdf

In this document we will go through the various user/kernel components of Linux native multipath.

The major user space components are libdevmapper shared library and multipathd daemon. The kernel modules of dm-mod and dm-multipath. Below Lower Level  Device Driver of libiscsi and scsi_transport_iscsi handle the submission to wire.


MULTIPATHD :

Lets first see the multipathd.

multipathd is installed in Linux systems like a service. The service start starts the daemon /etc/multipathd


Like any C program the main function of multipathd is invoked :
main --> Calls child
--> init_checkers
--> add_checker adds DIRECTIO checker
--> init_prio adds DEFAULT_PRIO
--> signal_init
--> set_oom_adj -- adjusts the OOM

Then it invokes 4 pthreads :
1. ueventloop --- Listens for the UDEV event in uevent_listen function. This adds the udev events to the queue
2. uxlsnrloop --- uxsock_listen waits for commands from users
3. checkerloop --- does check_path for all the available paths
4. uevqloop --- This takes out the events oushed in the queue.

Basically ueventloop listens for the events and uevqloop processes it.

uevqloop processes the UDEV event and eventually calls :
uev_trigger
--> uev_add_path is called for any ADD event
--> calls ev_add_path
--> calls domap

domap function for a new device creation calls dm_addmap_create
dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, 1, ro);
will call dm_task_create which is a libdevmapper function. This is similar to calling a dmsetup create command.

This creation will trigger an ioctl to the kernel space. This is done to device /dev/mapper/control device.


Linux Kernel life of the device creation :

Linux Kernel has the following misc driver created for the ioctl communication:

#ifdef CONFIG_COMPAT
static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
{
return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u));
}
#else
#define dm_compat_ctl_ioctl NULL
#endif

static const struct file_operations _ctl_fops = {
.open = nonseekable_open,
.unlocked_ioctl = dm_ctl_ioctl,
.compat_ioctl = dm_compat_ctl_ioctl,
.owner = THIS_MODULE,
.llseek  = noop_llseek,
};

static struct miscdevice _dm_misc = {
.minor = MAPPER_CTRL_MINOR,
.name  = DM_NAME,
.nodename = DM_DIR "/" DM_CONTROL_NODE,
.fops  = &_ctl_fops
};


dm_ctl_ioctl calls ctl_ioctl :
--> lookup_ioctl -- fetches the function corresponding to the device creation.
{DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_create}
dev_create calls dm_create
--> alloc_dev
--> dm_init_md_queue(md);
--> md->disk->fops = &dm_blk_dops;

static const struct block_device_operations dm_blk_dops = {
.open = dm_blk_open,
.release = dm_blk_close,
.ioctl = dm_blk_ioctl,
.direct_access = dm_blk_direct_access,
.getgeo = dm_blk_getgeo,
.pr_ops = &dm_pr_ops,
.owner = THIS_MODULE
};

Creation of /dev/dm-X device : 
Lets trace the creation of /dev/dm-X device.
Our favourite commands for the Ftrace :)

cd /sys/kernel/debug/tracing
cat /dev/null >  trace
echo dev_create > set_graph_function
echo 10 > max_graph_depth
echo function_graph > current_tracer
echo 1 > tracing_on
dmsetup create my_dev --table "0 14680064 multipath 2 queue_if_no_path retain_attached_hw_handler 1 alua 2 1 service-time 0 1 2 65:176 1 1 service-time 0 1 2 65:192 1 1"
cp trace ~/create_dev_depth10
echo 0 > tracing_on
echo > set_graph_function
echo 0 > max_graph_depth
cat /dev/null >  trace

Here is the trace :
 1)               |  dev_create [dm_mod]() {
 1)               |    dm_create [dm_mod]() {
 1)   0.035 us    |      dm_use_blk_mq_default [dm_mod]();
 1)   0.041 us    |      __mutex_init();
 1)   0.040 us    |      __mutex_init();
 1)   0.037 us    |      __mutex_init();
 1)               |      blk_alloc_queue_node() {
 1)               |        blkcg_init_queue() {
 1)   0.037 us    |          _cond_resched();
 1)               |          blk_throtl_init() {
 1)               |            kmem_cache_alloc_node_trace() {
 1)   0.042 us    |              _cond_resched();
 1)   0.482 us    |            }
 1)   0.036 us    |            init_timer_key();
 1)               |            blkcg_activate_policy() {
 1)   0.038 us    |              blkcg_policy_enabled();
 1)               |              blkg_alloc() {
 1)               |      alloc_disk_node() {
  1)               |        device_initialize() {
 1)   0.035 us    |          __mutex_init();
 1)               |          device_pm_sleep_init() {
 1)   0.034 us    |            __init_waitqueue_head();
 1)               |            complete_all() {
 1)   0.036 us    |              _raw_spin_lock_irqsave();
 1)   0.045 us    |              __wake_up_common();
 1)   0.046 us    |              _raw_spin_unlock_irqrestore();
 1)   0.980 us    |            }
 1)   1.645 us    |          }
 1)               |          pm_runtime_init() {
 1)   0.035 us    |            init_timer_key();
 1)   0.035 us    |            __init_waitqueue_head();
 1)   0.732 us    |          }
 1)   9.326 us    |        }
 1) + 17.547 us   |      }
 1)   0.035 us    |      __init_waitqueue_head();
 1)   0.034 us    |      __init_waitqueue_head();
 1)   0.035 us    |      __init_waitqueue_head();
 1)               |      add_disk() { ---------- This will send UDEV of KOBJ_ADD for creation of /dev/dm-X device
 1)   0.044 us    |        blk_alloc_devt();
 1)               |        bdi_register_dev() {
 1)               |      bdget_disk() {
 1)               |        disk_get_part() {
 1)   0.075 us    |          get_device();
 1)   0.575 us    |        }
 1)               |        bdget() {
 1)               |          iget5_locked() {
 1)   0.130 us    |            _raw_spin_lock();
 1)   0.285 us    |            find_inode();
 1)               |            alloc_inode() {
 1)               |      dm_stats_init [dm_mod]() {
 1)   0.035 us    |        __mutex_init();
 1)               |        __alloc_percpu() {
 1)               |          pcpu_alloc() {
 1)               |      dm_sysfs_init [dm_mod]() {
 1)   0.035 us    |        dm_disk [dm_mod]();
 1)   0.036 us    |        dm_kobject [dm_mod]();


{DM_TABLE_LOAD_CMD, 0, table_load}
 table_load function is called for initialising  the device.
r = dm_setup_md_queue(md, t);
dm_setup_md_queue -- calls dm_old_init_request_queue
Here request_fn for the device mapper is completely initialized.
blk_init_allocated_queue assigns the dm_request_fn
q->request_fn = rfn;
make_request_fn is initialised to blk_queue_bio

Now the function dm_init_normal_md_queue is called. This function initialises the softirq_done_fn with dm_softirq_done.
Also prep_rq_fn is initialized to dm_old_prep_fn.
At last this function calls elv_register_queue.

Yeah, why to look code flow we can ftrace it!! Here we go with the ftrace calls for table_load.

 1)               |  table_load [dm_mod]() {
 1)               |    find_device [dm_mod]() {
 1)               |      down_read() {
 1)   0.073 us    |        _cond_resched();
 1)   0.583 us    |      }
 1)               |      __find_device_hash_cell [dm_mod]() {
 1)               |        __get_name_cell [dm_mod]() {
 1)   0.123 us    |          dm_get [dm_mod]();
 1)   0.757 us    |        }
 1)   1.399 us    |      }
 1)   0.045 us    |      up_read();
 1)   3.060 us    |    }
 1)               |    dm_table_create [dm_mod]() {
 1)               |      kmem_cache_alloc_trace() {
 1)   0.040 us    |        _cond_resched();
 1)   0.499 us    |      }
 1)               |    dm_table_add_target [dm_mod]() {
 1)               |      dm_get_target_type [dm_mod]() {
 1)               |        get_target_type [dm_mod]() {
 1)               |          down_read() {
 1)   0.042 us    |            _cond_resched();
 1)   0.521 us    |          }
 1)   0.310 us    |          try_module_get();
 1)   0.045 us    |          up_read();
 1)   3.042 us    |        }
 1)   3.575 us    |      }
 1)               |      dm_split_args [dm_mod]() {
 1)               |        realloc_argv [dm_mod]() {
 1)               |          __kmalloc() {
 1)   0.058 us    |            kmalloc_slab();
 1)   0.040 us    |            _cond_resched();
 1)   0.776 us    |          }
 1)   0.051 us    |          kfree();
 1)   1.684 us    |        }
 1)   4.387 us    |      }
 1)               |      multipath_ctr [dm_multipath]() {
 1)   0.121 us    |        dm_get_reserved_rq_based_ios [dm_mod]();
 1)               |        kmem_cache_alloc_trace() {
 1)   0.041 us    |          _cond_resched();
 1)   0.416 us    |        }
 1)               |        ql_create [dm_queue_length]() {
 1)               |          kmem_cache_alloc_trace() {
 1)   0.040 us    |            _cond_resched();
 1)   0.475 us    |          }
 1)   0.807 us    |        }
 1)               |        ql_add_path [dm_queue_length]() {
 1)               |          kmem_cache_alloc_trace() {
 1)   0.042 us    |            _cond_resched();
 1)   0.391 us    |          }
 1)   0.951 us    |        }
 1)   0.041 us    |        dm_consume_args [dm_mod]();
 1) ! 539.424 us  |      }
 1)   0.134 us    |      kfree();
 1) ! 549.508 us  |    }
 0)   0.066 us    |    dm_get_immutable_target_type [dm_mod]();
 0)   0.093 us    |    dm_get_md_type [dm_mod]();
 0)   0.049 us    |    dm_table_get_type [dm_mod]();
 0)   0.044 us    |    dm_set_md_type [dm_mod]();
 0)               |    dm_setup_md_queue [dm_mod]() {
 0)               |      blk_init_allocated_queue() {
 0)               |        kmem_cache_alloc_trace() {
 0)   0.042 us    |          _cond_resched();
 0)   0.571 us    |        }
 0)               |        blk_init_rl() {
 0)               |        elevator_init() {
 0)               |          elevator_get() {
 0)   0.087 us    |            _raw_spin_lock();
 0)   0.391 us    |            elevator_find();
 0)   0.051 us    |            try_module_get();
 0)   0.040 us    |            _raw_spin_unlock();
 0)   1.862 us    |          }
 0)               |          deadline_init_queue() {
 0)               |            elevator_alloc() {
 0)               |              kmem_cache_alloc_node_trace() {
 0)   0.041 us    |                _cond_resched();
 0)   0.648 us    |              }
 0)   0.042 us    |              __mutex_init();
 0)   1.568 us    |            }
 0)               |            kmem_cache_alloc_node_trace() {
 0)   0.041 us    |              _cond_resched();
 0)   0.525 us    |            }
 0)   0.044 us    |            _raw_spin_lock_irq();
 0)   3.691 us    |          }
 0) + 45.638 us   |        }
 0)   0.047 us    |        mutex_unlock();
 0) + 62.761 us   |      }
 0)               |      dm_init_md_queue [dm_mod]() {
 0)               |        blk_queue_make_request() {
 0)   0.042 us    |          blk_queue_congestion_threshold();
 0)   0.044 us    |          blk_queue_bounce_limit();
 0)   0.721 us    |        }
 0)   0.044 us    |        blk_queue_bounce_limit();
 0)   0.042 us    |        blk_queue_merge_bvec();
 0)   1.827 us    |      }
 0)   0.043 us    |      blk_queue_softirq_done();
 0)   0.042 us    |      blk_queue_prep_rq();
 0)   0.042 us    |      blk_queue_lld_busy();
 0)               |      elv_register_queue() {
 0)               |    down_write() {
 0)   0.040 us    |      _cond_resched();
 0)   0.407 us    |    }
 0)   0.040 us    |    dm_get_mdptr [dm_mod]();
 0)   0.047 us    |    up_write();
 0)               |    __dev_status [dm_mod]() {
 0)   0.040 us    |      dm_disk [dm_mod]();
 0)   0.040 us    |      dm_suspended_md [dm_mod]();
 0)   0.040 us    |      dm_test_deferred_remove_flag [dm_mod]();
 0)   0.042 us    |      dm_open_count [dm_mod]();
 0)   0.047 us    |      dm_get_event_nr [dm_mod]();
 0)               |      dm_get_live_table [dm_mod]() {
 0)   0.134 us    |        __srcu_read_lock();
 0)   0.478 us    |      }
 0)               |      dm_put_live_table [dm_mod]() {
 0)   0.069 us    |        __srcu_read_unlock();
 0)   0.423 us    |      }
 0)   3.391 us    |    }
 0)   0.045 us    |    dm_put [dm_mod]();
 0) ! 3115.589 us |  } /* table_load [dm_mod] */


IO Flow:
Why are we doing all this, Yes for the IO flow.

Lets see how the IO Flows through our devmapper device. Now I am tracing dm_request_fn.

 0)               |  dm_request_fn [dm_mod]() {
 0)   0.240 us    |    __srcu_read_lock();
 0)               |    blk_peek_request() {
 0)               |      noop_dispatch() {
 0)   0.150 us    |        elv_dispatch_sort();
 0)   0.786 us    |      }
 0)               |      dm_prep_fn [dm_mod]() {
 0)               |        mempool_alloc() {
 0)               |          mempool_alloc_slab() {
 0)   0.412 us    |            kmem_cache_alloc();
 0)   1.019 us    |          }
 0)   1.589 us    |        }
 0)               |        blk_rq_prep_clone() {
 0)   0.280 us    |          blk_rq_init();
  0)   0.160 us    |    dm_table_find_target [dm_mod]();
 0)               |    multipath_busy [dm_multipath]() {
 0)   0.157 us    |      _raw_spin_lock_irqsave();
 0)               |      dm_underlying_device_busy [dm_mod]() {
 0)               |        blk_lld_busy() {
 0)   0.193 us    |          scsi_lld_busy();
 0)   1.056 us    |        }
 0)   1.905 us    |      }
 0)   0.096 us    |      _raw_spin_unlock_irqrestore();
 0)   5.042 us    |    }
 0)               |    dm_start_request [dm_mod]() {
 0)               |      blk_start_request() {
 0)   0.160 us    |        blk_dequeue_request();
 0)               |        blk_add_timer() {
 0)   0.180 us    |          __blk_add_timer();
 0)   1.032 us    |        }
 0)   2.910 us    |      }
 0)   0.150 us    |      dm_get [dm_mod]();
 0)   4.795 us    |    }
 0)   0.110 us    |    _raw_spin_unlock();
 0)               |    multipath_map [dm_multipath]() {
 0)               |      mempool_alloc() {
 0)               |        mempool_alloc_slab() {
 0)   0.193 us    |          kmem_cache_alloc();
 0)   1.152 us    |        }
 0)   2.015 us    |      }
 0)               |      map_io [dm_multipath]() {
 0)   0.107 us    |        _raw_spin_lock_irqsave();
 0)   0.136 us    |        ql_start_io [dm_queue_length]();
 0)   0.140 us    |        _raw_spin_unlock_irqrestore();
 0)   3.407 us    |      }
 0)   7.233 us    |    }
 0)               |    dm_dispatch_request [dm_mod]() {
 0)               |      blk_insert_cloned_request() {
 0)   0.120 us    |        blk_rq_check_limits();
 0)   0.103 us    |        _raw_spin_lock_irqsave();
 0)               |        blk_account_io_start() {
 0)   0.197 us    |          disk_map_sector_rcu();
 0)               |          part_round_stats() {
 0)   0.230 us    |            part_round_stats_single();
 0)   1.072 us    |          }
 0)   2.854 us    |        }
 0)               |        __elv_add_request() {
 0)               |          elv_drain_elevator() {
 0)   0.183 us    |            deadline_dispatch_requests();
 0)   1.003 us    |          }
 0)               |          __blk_run_queue() {
 0)               |            scsi_request_fn() {
 0)   0.277 us    |              get_device();
 0)               |              blk_peek_request() {
 0)               |                scsi_prep_fn() {
 0)               |                  scsi_get_command() {
 0)   0.100 us    |                    get_device();
 0)               |                    __scsi_get_command() {
 0)               |                      scsi_host_alloc_command() {
 0)               |                        scsi_pool_alloc_command() {
 0)   0.399 us    |                          kmem_cache_alloc();
 0)               |                          kmem_cache_alloc() {
 0)   0.922 us    |                            __slab_alloc();
 0)   1.871 us    |                          }
 0)   3.830 us    |                        }
 0)   4.778 us    |                      }
 0)   5.584 us    |                    }
 0)   0.163 us    |                    init_timer_key();
 0)   0.107 us    |                    _raw_spin_lock_irqsave();
 0)   0.110 us    |                    _raw_spin_unlock_irqrestore();
 0)   9.678 us    |                  }
 0)               |                  sd_init_command [sd_mod]() {
 0)               |                    scsi_setup_fs_cmnd() {
 0)   0.124 us    |                      alua_prep_fn();
 0)               |                      scsi_init_io() {
 0)               |                        scsi_init_sgtable() {
 0)               |                          scsi_alloc_sgtable() {
 0)               |                            scsi_sg_alloc() {
 0)               |                              mempool_alloc() {
 0)               |                                mempool_alloc_slab() {
 0)   0.300 us    |                                  kmem_cache_alloc();
 0)   1.112 us    |                                }
 0)   2.161 us    |                              }
 0)   2.964 us    |                            }
 0)   4.269 us    |                          }
0)               |              blk_start_request() {
 0)   0.210 us    |                blk_dequeue_request();
 0)               |                blk_add_timer() {
 0)               |                  __blk_add_timer() {
 0)   0.113 us    |                    round_jiffies_up();
 0)   1.482 us    |                  }
 0)   2.410 us    |                }
 0)   4.226 us    |              }
 0)   0.093 us    |              _raw_spin_unlock();
 0)   0.087 us    |              _raw_spin_lock();
 0)   0.403 us    |              scsi_init_cmd_errh();
 0)               |              scsi_dispatch_cmd() {
 0)   0.230 us    |                scsi_log_send();
 0)               |                iscsi_queuecommand [libiscsi]() {
 0)               |                  _raw_spin_lock_bh() {
 0)   0.074 us    |                    local_bh_disable();
 0)   0.702 us    |                  }
 0)               |                  iscsi_session_chkready [scsi_transport_iscsi]() {
 0)   0.086 us    |                    _raw_spin_lock_irqsave();
 0)   0.187 us    |                    _raw_spin_unlock_irqrestore();
 0)   1.562 us    |                  }
 0)   0.123 us    |                  queue_work_on();
 0)               |                  _raw_spin_unlock_bh() {
 0)   0.246 us    |                    local_bh_enable_ip();
 0)   0.839 us    |                  }
 0) + 17.190 us   |                }
 0) + 19.251 us   |              }
 0)   0.077 us    |              _raw_spin_lock_irq();
 0)               |              blk_peek_request() {
 0)   0.179 us    |                deadline_dispatch_requests();
 0)   0.873 us    |              }
 0)   0.130 us    |              put_device();
 0)   0.080 us    |              _raw_spin_lock_irq();
 0) + 87.287 us   |            }
 0) + 87.941 us   |          }
 0) + 90.672 us   |        }
 0)   0.096 us    |        _raw_spin_unlock_irqrestore();
 0) + 97.425 us   |      }
 0) + 98.507 us   |    }
 0)   0.077 us    |    _raw_spin_lock();
 0)               |    blk_peek_request() {
 0)   0.259 us    |      noop_dispatch();
 0)   1.063 us    |    }
 0)               |    blk_delay_queue() {
 0)   0.080 us    |      msecs_to_jiffies();
 0)   0.163 us    |      queue_delayed_work_on();
 0)   1.305 us    |    }
 0)   0.169 us    |    __srcu_read_unlock();
 0) ! 290.409 us  |  }


No comments:

Post a Comment