qemu_rbd_open流程分析




qemu进程启动参数:
./qemu-system-x86_64 -m 512 -smp 1 -drive file=rbd:rbd/vol1:auth_supported=none:mon_host=192.168.0.2\\:6789,cache=none,if=none,format=raw

// qemu\block\rbd.c
static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
                         Error **errp)
{
    BDRVRBDState *s = bs->opaque;
    char pool[RBD_MAX_POOL_NAME_SIZE];
    char snap_buf[RBD_MAX_SNAP_NAME_SIZE];
    char conf[RBD_MAX_CONF_SIZE];
    char clientname_buf[RBD_MAX_CONF_SIZE];
    char *clientname;
    const char *secretid;
    QemuOpts *opts;
    Error *local_err = NULL;
    const char *filename;
    int r;

    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); // 初始化opts结构体
    qemu_opts_absorb_qdict(opts, options, &local_err);  // 把options转成opts
    if (local_err) {
        error_propagate(errp, local_err);
        qemu_opts_del(opts);
        return -EINVAL;
    }

    filename = qemu_opt_get(opts, "filename");  // "rbd:rbd/vol1:auth_supported=none:mon_host=192.168.0.2\\:6789"
    secretid = qemu_opt_get(opts, "password-secret"); // 0x0

    if (qemu_rbd_parsename(filename, pool, sizeof(pool), // 从filename中解析pool、snap、name、conf,其中pool是存储池,snap是快照名,name是卷名,conf是配置文件路径
                           snap_buf, sizeof(snap_buf),
                           s->name, sizeof(s->name),
                           conf, sizeof(conf), errp) < 0) {
        r = -EINVAL;
        goto failed_opts;
    }

    clientname = qemu_rbd_parse_clientname(conf, clientname_buf); // clientname=0x0,qemu没有传这个参数,为空
    r = rados_create(&s->cluster, clientname);
    if (r < 0) {
        error_setg_errno(errp, -r, "error initializing");
        goto failed_opts;
    }

    s->snap = NULL;
    if (snap_buf[0] != '\0') {
        s->snap = g_strdup(snap_buf);
    }

    if (strstr(conf, "conf=") == NULL) {
        /* try default location, but ignore failure */
        rados_conf_read_file(s->cluster, NULL);  // 走这里,命令行没有传“conf=”参数,librados有默认路径
    } else if (conf[0] != '\0') {
        r = qemu_rbd_set_conf(s->cluster, conf, true, errp);
        if (r < 0) {
            goto failed_shutdown;
        }
    }

    if (conf[0] != '\0') {
        r = qemu_rbd_set_conf(s->cluster, conf, false, errp); // 把conf中的参数传给librados的_conf对象,这里会覆盖上面从配置文件中读取的配置项,如mon_host
        if (r < 0) {
            goto failed_shutdown;
        }
    }

    if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {  // secretid为空
        r = -EIO;
        goto failed_shutdown;
    }

    /*
     * Fallback to more conservative semantics if setting cache
     * options fails. Ignore errors from setting rbd_cache because the
     * only possible error is that the option does not exist, and
     * librbd defaults to no caching. If write through caching cannot
     * be set up, fall back to no caching.
     */
    if (flags & BDRV_O_NOCACHE) {
        rados_conf_set(s->cluster, "rbd_cache", "false"); // 这里,这个flags估计要传递给part2
    } else {
        rados_conf_set(s->cluster, "rbd_cache", "true");
    }

    r = rados_connect(s->cluster);
    if (r < 0) {
        error_setg_errno(errp, -r, "error connecting");
        goto failed_shutdown;
    }

    r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
    if (r < 0) {
        error_setg_errno(errp, -r, "error opening pool %s", pool);
        goto failed_shutdown;
    }

    r = rbd_open(s->io_ctx, s->name, &s->image, s->snap);
    if (r < 0) {
        error_setg_errno(errp, -r, "error reading header from %s", s->name);
        goto failed_open;
    }

    bs->read_only = (s->snap != NULL);

    qemu_opts_del(opts);
    return 0;

failed_open:
    rados_ioctx_destroy(s->io_ctx);
failed_shutdown:
    rados_shutdown(s->cluster);
    g_free(s->snap);
failed_opts:
    qemu_opts_del(opts);
    return r;
}
// src\librados\librados.cc
// -- config --
extern "C" int rados_conf_read_file(rados_t cluster, const char *path_list)
{
  tracepoint(librados, rados_conf_read_file_enter, cluster, path_list);
  librados::RadosClient *client = (librados::RadosClient *)cluster;
  md_config_t *conf = client->cct->_conf;
  std::deque<std::string> parse_errors;
  int ret = conf->parse_config_files(path_list, &parse_errors, NULL, 0);
  if (ret) {
    tracepoint(librados, rados_conf_read_file_exit, ret);
    return ret;
  }
  conf->parse_env(); // environment variables override

  conf->apply_changes(NULL);
  complain_about_parse_errors(client->cct, &parse_errors);
  tracepoint(librados, rados_conf_read_file_exit, 0);
  return 0;
}

// src\common\config.cc int md_config_t::parse_config_files(const char *conf_files, std::deque<std::string> *parse_errors, std::ostream *warnings, int flags) { Mutex::Locker l(lock); if (internal_safe_to_start_threads) return -ENOSYS; if (!conf_files) { const char *c = getenv("CEPH_CONF"); if (c) { conf_files = c; } else { if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE) return 0; conf_files = CEPH_CONF_FILE_DEFAULT; // const char *CEPH_CONF_FILE_DEFAULT = "/etc/ceph/$cluster.conf, ~/.ceph/$cluster.conf, $cluster.conf"; } } std::list</std::string><std::string> cfl; get_str_list(conf_files, cfl); // cfl = {[0] = "/etc/ceph/$cluster.conf", [1] = "~/.ceph/$cluster.conf", [2] = "$cluster.conf"} return parse_config_files_impl(cfl, parse_errors, warnings); } int md_config_t::parse_config_files_impl(const std::list</std::string><std::string> &conf_files, std::deque</std::string><std::string> *parse_errors, std::ostream *warnings) { assert(lock.is_locked()); // open new conf list<string>::const_iterator c; for (c = conf_files.begin(); c != conf_files.end(); ++c) { cf.clear(); string fn = *c; expand_meta(fn, warnings); int ret = cf.parse_file(fn.c_str(), parse_errors, warnings); if (ret == 0) break; // 读取到第一个配置文件就不再读取后面的,/etc/ceph/$cluster.conf,其中$cluster会被替换为默认值ceph else if (ret != -ENOENT) return ret; } if (c == conf_files.end()) return -EINVAL; std::vector <std::string> my_sections; _get_my_sections(my_sections); // my_sections = {"client.admin", "client", "global"},正常会读[client]和[global]两个段下的配置项 for (int i = 0; i < NUM_CONFIG_OPTIONS; i++) { config_option *opt = &config_optionsp[i]; std::string val; int ret = _get_val_from_conf_file(my_sections, opt->name, val, false); // 从配置文件中读取配置项 if (ret == 0) { set_val_impl(val.c_str(), opt); // 设置配置项的值,比如mon_host = 192.168.0.2 } } // subsystems?日志配置项 for (int o = 0; o < subsys.get_num(); o++) { std::string as_option("debug_"); as_option += subsys.get_name(o); std::string val; int ret = _get_val_from_conf_file(my_sections, as_option.c_str(), val, false); if (ret == 0) { int log, gather; int r = sscanf(val.c_str(), "%d/%d", &log, &gather); if (r >= 1) { if (r < 2) gather = log; // cout << "config subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl; subsys.set_log_level(o, log); subsys.set_gather_level(o, gather); } } } // Warn about section names that look like old-style section names std::deque < std::string > old_style_section_names; for (ConfFile::const_section_iter_t s = cf.sections_begin(); s != cf.sections_end(); ++s) { const string &str(s->first); if (((str.find("mds") == 0) || (str.find("mon") == 0) || (str.find("osd") == 0)) && (str.size() > 3) && (str[3] != '.')) { old_style_section_names.push_back(str); } } if (!old_style_section_names.empty()) { ostringstream oss; oss < < "ERROR! old-style section name(s) found: "; string sep; for (std::deque < std::string >::const_iterator os = old_style_section_names.begin(); os != old_style_section_names.end(); ++os) { oss < < sep << *os; sep = ", "; } oss << ". Please use the new style section names that include a period."; parse_errors->push_back(oss.str()); } return 0; }

“`