Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/fs/nfs/filelayout/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 8 kB image not shown  

Quelle  disk_groups.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
#include "bcachefs.h"
#include "disk_groups.h"
#include "sb-members.h"
#include "super-io.h"

#include <linux/sort.h>

static int group_cmp(const void *_l, const void *_r)
{
 const struct bch_disk_group *l = _l;
 const struct bch_disk_group *r = _r;

 return ((BCH_GROUP_DELETED(l) > BCH_GROUP_DELETED(r)) -
  (BCH_GROUP_DELETED(l) < BCH_GROUP_DELETED(r))) ?:
  ((BCH_GROUP_PARENT(l) > BCH_GROUP_PARENT(r)) -
   (BCH_GROUP_PARENT(l) < BCH_GROUP_PARENT(r))) ?:
  strncmp(l->label, r->label, sizeof(l->label));
}

static int bch2_sb_disk_groups_validate(struct bch_sb *sb, struct bch_sb_field *f,
    enum bch_validate_flags flags, struct printbuf *err)
{
 struct bch_sb_field_disk_groups *groups =
  field_to_type(f, disk_groups);
 struct bch_disk_group *g, *sorted = NULL;
 unsigned nr_groups = disk_groups_nr(groups);
 unsigned i, len;
 int ret = 0;

 for (i = 0; i < sb->nr_devices; i++) {
  struct bch_member m = bch2_sb_member_get(sb, i);
  unsigned group_id;

  if (!BCH_MEMBER_GROUP(&m))
   continue;

  group_id = BCH_MEMBER_GROUP(&m) - 1;

  if (group_id >= nr_groups) {
   prt_printf(err, "disk %u has invalid label %u (have %u)",
       i, group_id, nr_groups);
   return -BCH_ERR_invalid_sb_disk_groups;
  }

  if (BCH_GROUP_DELETED(&groups->entries[group_id])) {
   prt_printf(err, "disk %u has deleted label %u", i, group_id);
   return -BCH_ERR_invalid_sb_disk_groups;
  }
 }

 if (!nr_groups)
  return 0;

 for (i = 0; i < nr_groups; i++) {
  g = groups->entries + i;

  if (BCH_GROUP_DELETED(g))
   continue;

  len = strnlen(g->label, sizeof(g->label));
  if (!len) {
   prt_printf(err, "label %u empty", i);
   return -BCH_ERR_invalid_sb_disk_groups;
  }
 }

 sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);
 if (!sorted)
  return -BCH_ERR_ENOMEM_disk_groups_validate;

 memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));
 sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL);

 for (g = sorted; g + 1 < sorted + nr_groups; g++)
  if (!BCH_GROUP_DELETED(g) &&
      !group_cmp(&g[0], &g[1])) {
   prt_printf(err, "duplicate label %llu.%.*s",
          BCH_GROUP_PARENT(g),
          (intsizeof(g->label), g->label);
   ret = -BCH_ERR_invalid_sb_disk_groups;
   goto err;
  }
err:
 kfree(sorted);
 return ret;
}

static void bch2_sb_disk_groups_to_text(struct printbuf *out,
     struct bch_sb *sb,
     struct bch_sb_field *f)
{
 struct bch_sb_field_disk_groups *groups =
  field_to_type(f, disk_groups);
 struct bch_disk_group *g;
 unsigned nr_groups = disk_groups_nr(groups);

 for (g = groups->entries;
      g < groups->entries + nr_groups;
      g++) {
  if (g != groups->entries)
   prt_printf(out, " ");

  if (BCH_GROUP_DELETED(g))
   prt_printf(out, "[deleted]");
  else
   prt_printf(out, "[parent %llu name %s]",
          BCH_GROUP_PARENT(g), g->label);
 }
}

const struct bch_sb_field_ops bch_sb_field_ops_disk_groups = {
 .validate = bch2_sb_disk_groups_validate,
 .to_text = bch2_sb_disk_groups_to_text
};

int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
{
 struct bch_sb_field_disk_groups *groups;
 struct bch_disk_groups_cpu *cpu_g, *old_g;
 unsigned i, g, nr_groups;

 lockdep_assert_held(&c->sb_lock);

 groups  = bch2_sb_field_get(c->disk_sb.sb, disk_groups);
 nr_groups = disk_groups_nr(groups);

 if (!groups)
  return 0;

 cpu_g = kzalloc(struct_size(cpu_g, entries, nr_groups), GFP_KERNEL);
 if (!cpu_g)
  return bch_err_throw(c, ENOMEM_disk_groups_to_cpu);

 cpu_g->nr = nr_groups;

 for (i = 0; i < nr_groups; i++) {
  struct bch_disk_group *src = &groups->entries[i];
  struct bch_disk_group_cpu *dst = &cpu_g->entries[i];

  dst->deleted = BCH_GROUP_DELETED(src);
  dst->parent = BCH_GROUP_PARENT(src);
  memcpy(dst->label, src->label, sizeof(dst->label));
 }

 for (i = 0; i < c->disk_sb.sb->nr_devices; i++) {
  struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, i);
  struct bch_disk_group_cpu *dst;

  if (!bch2_member_alive(&m))
   continue;

  g = BCH_MEMBER_GROUP(&m);
  while (g) {
   dst = &cpu_g->entries[g - 1];
   __set_bit(i, dst->devs.d);
   g = dst->parent;
  }
 }

 old_g = rcu_dereference_protected(c->disk_groups,
    lockdep_is_held(&c->sb_lock));
 rcu_assign_pointer(c->disk_groups, cpu_g);
 if (old_g)
  kfree_rcu(old_g, rcu);

 return 0;
}

const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target)
{
 struct target t = target_decode(target);

 guard(rcu)();

 switch (t.type) {
 case TARGET_NULL:
  return NULL;
 case TARGET_DEV: {
  struct bch_dev *ca = t.dev < c->sb.nr_devices
   ? rcu_dereference(c->devs[t.dev])
   : NULL;
  return ca ? &ca->self : NULL;
 }
 case TARGET_GROUP: {
  struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);

  return g && t.group < g->nr && !g->entries[t.group].deleted
   ? &g->entries[t.group].devs
   : NULL;
 }
 default:
  BUG();
 }
}

bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target)
{
 struct target t = target_decode(target);

 switch (t.type) {
 case TARGET_NULL:
  return false;
 case TARGET_DEV:
  return dev == t.dev;
 case TARGET_GROUP: {
  struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
  const struct bch_devs_mask *m =
   g && t.group < g->nr && !g->entries[t.group].deleted
   ? &g->entries[t.group].devs
   : NULL;

  return m ? test_bit(dev, m->d) : false;
 }
 default:
  BUG();
 }
}

static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,
      unsigned parent,
      const char *name, unsigned namelen)
{
 unsigned i, nr_groups = disk_groups_nr(groups);

 if (!namelen || namelen > BCH_SB_LABEL_SIZE)
  return -EINVAL;

 for (i = 0; i < nr_groups; i++) {
  struct bch_disk_group *g = groups->entries + i;

  if (BCH_GROUP_DELETED(g))
   continue;

  if (!BCH_GROUP_DELETED(g) &&
      BCH_GROUP_PARENT(g) == parent &&
      strnlen(g->label, sizeof(g->label)) == namelen &&
      !memcmp(name, g->label, namelen))
   return i;
 }

 return -1;
}

static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,
     const char *name, unsigned namelen)
{
 struct bch_sb_field_disk_groups *groups =
  bch2_sb_field_get(sb->sb, disk_groups);
 unsigned i, nr_groups = disk_groups_nr(groups);
 struct bch_disk_group *g;

 if (!namelen || namelen > BCH_SB_LABEL_SIZE)
  return -EINVAL;

 for (i = 0;
      i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);
      i++)
  ;

 if (i == nr_groups) {
  unsigned u64s =
   (sizeof(struct bch_sb_field_disk_groups) +
    sizeof(struct bch_disk_group) * (nr_groups + 1)) /
   sizeof(u64);

  groups = bch2_sb_field_resize(sb, disk_groups, u64s);
  if (!groups)
   return -BCH_ERR_ENOSPC_disk_label_add;

  nr_groups = disk_groups_nr(groups);
 }

 BUG_ON(i >= nr_groups);

 g = &groups->entries[i];

 memcpy(g->label, name, namelen);
 if (namelen < sizeof(g->label))
  g->label[namelen] = '\0';
 SET_BCH_GROUP_DELETED(g, 0);
 SET_BCH_GROUP_PARENT(g, parent);
 SET_BCH_GROUP_DATA_ALLOWED(g, ~0);

 return i;
}

int bch2_disk_path_find(struct bch_sb_handle *sb, const char *name)
{
 struct bch_sb_field_disk_groups *groups =
  bch2_sb_field_get(sb->sb, disk_groups);
 int v = -1;

 do {
  const char *next = strchrnul(name, '.');
  unsigned len = next - name;

  if (*next == '.')
   next++;

  v = __bch2_disk_group_find(groups, v + 1, name, len);
  name = next;
 } while (*name && v >= 0);

 return v;
}

int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)
{
 struct bch_sb_field_disk_groups *groups;
 unsigned parent = 0;
 int v = -1;

 do {
  const char *next = strchrnul(name, '.');
  unsigned len = next - name;

  if (*next == '.')
   next++;

  groups = bch2_sb_field_get(sb->sb, disk_groups);

  v = __bch2_disk_group_find(groups, parent, name, len);
  if (v < 0)
   v = __bch2_disk_group_add(sb, parent, name, len);
  if (v < 0)
   return v;

  parent = v + 1;
  name = next;
 } while (*name && v >= 0);

 return v;
}

static void __bch2_disk_path_to_text(struct printbuf *out, struct bch_disk_groups_cpu *g,
         unsigned v)
{
 u16 path[32];
 unsigned nr = 0;

 while (1) {
  if (nr == ARRAY_SIZE(path))
   goto invalid;

  if (v >= (g ? g->nr : 0))
   goto invalid;

  struct bch_disk_group_cpu *e = g->entries + v;

  if (e->deleted)
   goto invalid;

  path[nr++] = v;

  if (!e->parent)
   break;

  v = e->parent - 1;
 }

 while (nr) {
  struct bch_disk_group_cpu *e = g->entries + path[--nr];

  prt_printf(out, "%.*s", (intsizeof(e->label), e->label);
  if (nr)
   prt_printf(out, ".");
 }
 return;
invalid:
 prt_printf(out, "invalid label %u", v);
}

void bch2_disk_groups_to_text(struct printbuf *out, struct bch_fs *c)
{
 bch2_printbuf_make_room(out, 4096);

 out->atomic++;
 guard(rcu)();
 struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);

 for (unsigned i = 0; i < (g ? g->nr : 0); i++) {
  prt_printf(out, "%2u: ", i);

  if (g->entries[i].deleted) {
   prt_printf(out, "[deleted]");
   goto next;
  }

  __bch2_disk_path_to_text(out, g, i);

  prt_printf(out, " devs");

  for_each_member_device_rcu(c, ca, &g->entries[i].devs)
   prt_printf(out, " %s", ca->name);
next:
  prt_newline(out);
 }

 out->atomic--;
}

void bch2_disk_path_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)
{
 out->atomic++;
 guard(rcu)();
 __bch2_disk_path_to_text(out, rcu_dereference(c->disk_groups), v),
 --out->atomic;
}

void bch2_disk_path_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)
{
 struct bch_sb_field_disk_groups *groups =
  bch2_sb_field_get(sb, disk_groups);
 struct bch_disk_group *g;
 unsigned nr = 0;
 u16 path[32];

 while (1) {
  if (nr == ARRAY_SIZE(path))
   goto inval;

  if (v >= disk_groups_nr(groups))
   goto inval;

  g = groups->entries + v;

  if (BCH_GROUP_DELETED(g))
   goto inval;

  path[nr++] = v;

  if (!BCH_GROUP_PARENT(g))
   break;

  v = BCH_GROUP_PARENT(g) - 1;
 }

 while (nr) {
  v = path[--nr];
  g = groups->entries + v;

  prt_printf(out, "%.*s", (intsizeof(g->label), g->label);
  if (nr)
   prt_printf(out, ".");
 }
 return;
inval:
 prt_printf(out, "invalid label %u", v);
}

int __bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
{
 lockdep_assert_held(&c->sb_lock);


 if (!strlen(name) || !strcmp(name, "none")) {
  struct bch_member *mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
  SET_BCH_MEMBER_GROUP(mi, 0);
 } else {
  int v = bch2_disk_path_find_or_create(&c->disk_sb, name);
  if (v < 0)
   return v;

  struct bch_member *mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
  SET_BCH_MEMBER_GROUP(mi, v + 1);
 }

 return bch2_sb_disk_groups_to_cpu(c);
}

int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
{
 int ret;

 mutex_lock(&c->sb_lock);
 ret = __bch2_dev_group_set(c, ca, name) ?:
  bch2_write_super(c);
 mutex_unlock(&c->sb_lock);

 return ret;
}

int bch2_opt_target_parse(struct bch_fs *c, const char *val, u64 *res,
     struct printbuf *err)
{
 struct bch_dev *ca;
 int g;

 if (!val)
  return -EINVAL;

 if (!c)
  return -BCH_ERR_option_needs_open_fs;

 if (!strlen(val) || !strcmp(val, "none")) {
  *res = 0;
  return 0;
 }

 /* Is it a device? */
 ca = bch2_dev_lookup(c, val);
 if (!IS_ERR(ca)) {
  *res = dev_to_target(ca->dev_idx);
  bch2_dev_put(ca);
  return 0;
 }

 mutex_lock(&c->sb_lock);
 g = bch2_disk_path_find(&c->disk_sb, val);
 mutex_unlock(&c->sb_lock);

 if (g >= 0) {
  *res = group_to_target(g);
  return 0;
 }

 return -EINVAL;
}

void bch2_target_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)
{
 struct target t = target_decode(v);

 switch (t.type) {
 case TARGET_NULL:
  prt_printf(out, "none");
  return;
 case TARGET_DEV: {
  out->atomic++;
  guard(rcu)();
  struct bch_dev *ca = t.dev < c->sb.nr_devices
   ? rcu_dereference(c->devs[t.dev])
   : NULL;

  if (ca && ca->disk_sb.bdev)
   prt_printf(out, "/dev/%s", ca->name);
  else if (ca)
   prt_printf(out, "offline device %u", t.dev);
  else
   prt_printf(out, "invalid device %u", t.dev);

  out->atomic--;
  return;
 }
 case TARGET_GROUP:
  bch2_disk_path_to_text(out, c, t.group);
  return;
 default:
  BUG();
 }
}

static void bch2_target_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)
{
 struct target t = target_decode(v);

 switch (t.type) {
 case TARGET_NULL:
  prt_printf(out, "none");
  break;
 case TARGET_DEV: {
  struct bch_member m = bch2_sb_member_get(sb, t.dev);

  if (bch2_member_exists(sb, t.dev)) {
   prt_printf(out, "Device ");
   pr_uuid(out, m.uuid.b);
   prt_printf(out, " (%u)", t.dev);
  } else {
   prt_printf(out, "Bad device %u", t.dev);
  }
  break;
 }
 case TARGET_GROUP:
  bch2_disk_path_to_text_sb(out, sb, t.group);
  break;
 default:
  BUG();
 }
}

void bch2_opt_target_to_text(struct printbuf *out,
        struct bch_fs *c,
        struct bch_sb *sb,
        u64 v)
{
 if (c)
  bch2_target_to_text(out, c, v);
 else
  bch2_target_to_text_sb(out, sb, v);
}

Messung V0.5
C=100 H=85 G=92

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.