2012年8月5日日曜日

[LVM] file descriptor leaked on lvremove invocation

あらまし


LVMのLV情報出力コマンド

lvdisplay -C

を打っていたらfile descriptor leakedのwarningが返ってきた

File descriptor 6 (/home/..../log/backup.log) leaked on lvcreate invocation. Parent PID 15481: ruby
File descriptor 7 (socket:[79943]) leaked on lvcreate invocation. Parent PID 15481: ruby

動作自体は問題なさそうだが、念のため調査しておくことにした

file descriptorとは?




ふむふむ。そして問題有無については、結論からいうと問題なさそうだ
  • 他のファイルがオープンしていたりする際に、このwarningがでる
  • lvmの1.5.2以下のバグで、1.6.1でfixされている
File descriptor leaked on lvm invocation | Eucalyptus Community

2012年7月15日日曜日

LVM2 ソースコードリーディング その1

lvmについての知識を深めるべく、ソースコードリーディング開始

コードはこちら

http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=lvm2

良く使い短めのソースから見てみることにする


構成


まずはディレクトリ構成から

http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=lvm2


toolsフォルダの中身を見れば、良く知っているコマンド名のファイルがたくさんあるのでそれを見ればよさそう

http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/tools/?cvsroot=lvm2

pvcreate (LVM2/tools/pvcreate.c)


一番シンプルなpvcreate.cから

関数は2つしかなくていたってシンプル

static int pvcreate_restore_params_validate(struct cmd_context *cmd,
                                            int argc, char **argv,
                                            struct pvcreate_params *pp)
int pvcreate(struct cmd_context *cmd, int argc, char **argv)
そしてpvcreate関数はたったこれだけ

int pvcreate(struct cmd_context *cmd, int argc, char **argv)
{
        int i;
        int ret = ECMD_PROCESSED;
        struct pvcreate_params pp;
        struct physical_volume *pv;

        pvcreate_params_set_defaults(&pp);

        if (!pvcreate_restore_params_validate(cmd, argc, argv, &pp)) {
                return EINVALID_CMD_LINE;
        }
        if (!pvcreate_params_validate(cmd, argc, argv, &pp)) {
                return EINVALID_CMD_LINE;
        }

        for (i = 0; i < argc; i++) {
                if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
                        log_error("Can't get lock for orphan PVs");
                        return ECMD_FAILED;
                }

                dm_unescape_colons_and_at_signs(argv[i], NULL, NULL);

                if (!(pv = pvcreate_single(cmd, argv[i], &pp, 1))) {
                        stack;
                        ret = ECMD_FAILED;
                }

                unlock_vg(cmd, VG_ORPHANS);
                if (sigint_caught())
                        return ret;
        }

        return ret;
}


大体下記の流れ
  • 入力チェック
  • PV1つずつ以下の処理
    • ロック取得
    • PV作成
    • ロック解放
入力チェック 関数の返り値はこれ
#define ECMD_PROCESSED          1
#define ENO_SUCH_CMD            2
#define EINVALID_CMD_LINE       3
#define ECMD_FAILED             5
※LVM2/lib/commands/errors.h

ロックとか、入力の構造体とかはすっとばして、pv_create_singleを探す。
※LVM2/lib/metadata/metadata.c
/*
 * pvcreate_single() - initialize a device with PV label and metadata area
 *
 * Parameters:
 * - pv_name: device path to initialize
 * - pp: parameters to pass to pv_create; if NULL, use default values
 *
 * Returns:
 * NULL: error
 * struct physical_volume * (non-NULL): handle to physical volume created
 */
struct physical_volume * pvcreate_single(struct cmd_context *cmd,
      const char *pv_name,
      struct pvcreate_params *pp,
      int write_now)
{
 struct physical_volume *pv = NULL;
 struct device *dev;
 struct dm_list mdas;
 struct pvcreate_params default_pp;
 char buffer[64] __attribute__((aligned(8)));

 pvcreate_params_set_defaults(&default_pp);
 if (!pp)
  pp = &default_pp;

 if (pp->idp) {
  if ((dev = lvmcache_device_from_pvid(cmd, pp->idp, NULL, NULL)) &&
      (dev != dev_cache_get(pv_name, cmd->filter))) {
   if (!id_write_format((const struct id*)&pp->idp->uuid,
       buffer, sizeof(buffer)))
    goto_bad;
   log_error("uuid %s already in use on \"%s\"", buffer,
      dev_name(dev));
   goto bad;;
  }
 }

 if (!pvcreate_check(cmd, pv_name, pp))
  goto_bad;

 if (sigint_caught())
  goto_bad;

 if (!(dev = dev_cache_get(pv_name, cmd->filter))) {
  log_error("%s: Couldn't find device.  Check your filters?",
     pv_name);
  goto bad;
 }

 dm_list_init(&mdas);

 if (!(pv = pv_create(cmd, dev, pp->idp, pp->size,
        pp->data_alignment, pp->data_alignment_offset,
        pp->pe_start ? pp->pe_start : PV_PE_START_CALC,
        pp->extent_count, pp->extent_size,
        pp->labelsector, pp->pvmetadatacopies,
        pp->pvmetadatasize, pp->metadataignore))) {
  log_error("Failed to setup physical volume \"%s\"", pv_name);
  goto bad;
 }

 log_verbose("Set up physical volume for \"%s\" with %" PRIu64
      " available sectors", pv_name, pv_size(pv));

 if (write_now) {
  struct pv_to_create pvc;
  pvc.pp = pp;
  pvc.pv = pv;
  if (!_pvcreate_write(cmd, &pvc))
   goto bad;
 } else {
  pv->status |= UNLABELLED_PV;
 }

 return pv;

bad:
 return NULL;
}
/**
 * pv_create - initialize a physical volume for use with a volume group
 * created PV belongs to Orphan VG.
 *
 * @fmt: format type
 * @dev: PV device to initialize
 * @size: size of the PV in sectors
 * @data_alignment: requested alignment of data
 * @data_alignment_offset: requested offset to aligned data
 * @pe_start: physical extent start
 * @existing_extent_count
 * @existing_extent_size
 * @pvmetadatacopies
 * @pvmetadatasize
 * @mdas
 *
 * Returns:
 *   PV handle - physical volume initialized successfully
 *   NULL - invalid parameter or problem initializing the physical volume
 *
 * Note:
 *   FIXME: shorten argument list and replace with explict 'set' functions
 */
struct physical_volume *pv_create(const struct cmd_context *cmd,
      struct device *dev,
      struct id *id, uint64_t size,
      unsigned long data_alignment,
      unsigned long data_alignment_offset,
      uint64_t pe_start,
      uint32_t existing_extent_count,
      uint32_t existing_extent_size,
      uint64_t label_sector,
      unsigned pvmetadatacopies,
      uint64_t pvmetadatasize,
      unsigned metadataignore)
{
 const struct format_type *fmt = cmd->fmt;
 struct dm_pool *mem = fmt->orphan_vg->vgmem;
 struct physical_volume *pv = _alloc_pv(mem, dev);
 unsigned mda_index;
 struct pv_list *pvl;

 if (!pv)
  return_NULL;

 if (id)
  memcpy(&pv->id, id, sizeof(*id));
 else if (!id_create(&pv->id)) {
  log_error("Failed to create random uuid for %s.",
     dev_name(dev));
  goto bad;
 }

 if (!dev_get_size(pv->dev, &pv->size)) {
  log_error("%s: Couldn't get size.", pv_dev_name(pv));
  goto bad;
 }

 if (size) {
  if (size > pv->size)
   log_warn("WARNING: %s: Overriding real size. "
      "You could lose data.", pv_dev_name(pv));
  log_verbose("%s: Pretending size is %" PRIu64 " sectors.",
       pv_dev_name(pv), size);
  pv->size = size;
 }

 if (pv->size < pv_min_size()) {
  log_error("%s: Size must exceed minimum of %" PRIu64 " sectors.",
     pv_dev_name(pv), pv_min_size());
  goto bad;
 }

 if (pv->size < data_alignment) {
  log_error("%s: Data alignment must not exceed device size.",
     pv_dev_name(pv));
  goto bad;
 }

 if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
  log_error("pv_list allocation in pv_create failed");
  goto bad;
 }

 pvl->pv = pv;
 add_pvl_to_vgs(fmt->orphan_vg, pvl);
 fmt->orphan_vg->extent_count += pv->pe_count;
 fmt->orphan_vg->free_count += pv->pe_count;

 pv->fmt = fmt;
 pv->vg_name = fmt->orphan_vg_name;

 if (!fmt->ops->pv_initialise(fmt, label_sector, pe_start,
         existing_extent_count, existing_extent_size,
         data_alignment, data_alignment_offset, pv)) {
  log_error("Format-specific initialisation of physical "
     "volume %s failed.", pv_dev_name(pv));
  goto bad;
 }

 for (mda_index = 0; mda_index < pvmetadatacopies; mda_index++) {
  if (pv->fmt->ops->pv_add_metadata_area &&
      !pv->fmt->ops->pv_add_metadata_area(pv->fmt, pv,
     pe_start != PV_PE_START_CALC,
     mda_index, pvmetadatasize,
     metadataignore)) {
   log_error("Failed to add metadata area for "
      "new physical volume %s", pv_dev_name(pv));
   goto bad;
  }
 }

 return pv;

      bad:
 // FIXME: detach from orphan in error path
 //free_pv_fid(pv);
 //dm_pool_free(mem, pv);
 return NULL;
}
LVM2/lib/metadata/lv_alloc.h

memo


- Orphan PV is device with PV label which is not attached to any Volume Group.