summaryrefslogtreecommitdiffstats
path: root/src/vnet/ethernet/sfp.c
blob: 0ebdb99339b5d90e0b34614d1cf56f87cdf5a95d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/ethernet/sfp.h>

static u8 *
format_space_terminated (u8 * s, va_list * args)
{
  u32 l = va_arg (*args, u32);
  u8 *v = va_arg (*args, u8 *);
  u8 *p;

  for (p = v + l - 1; p >= v && p[0] == ' '; p--)
    ;
  vec_add (s, v, clib_min (p - v + 1, l));
  return s;
}

static u8 *
format_sfp_id (u8 * s, va_list * args)
{
  u32 id = va_arg (*args, u32);
  char *t = 0;
  switch (id)
    {
#define _(f) case SFP_ID_##f: t = #f; break;
      foreach_sfp_id
#undef _
    default:
      return format (s, "unknown 0x%x", id);
    }
  return format (s, "%s", t);
}

static u8 *
format_sfp_compatibility (u8 * s, va_list * args)
{
  u32 c = va_arg (*args, u32);
  char *t = 0;
  switch (c)
    {
#define _(a,b,f) case SFP_COMPATIBILITY_##f: t = #f; break;
      foreach_sfp_compatibility
#undef _
    default:
      return format (s, "unknown 0x%x", c);
    }
  return format (s, "%s", t);
}

u32
sfp_is_comatible (sfp_eeprom_t * e, sfp_compatibility_t c)
{
  static struct
  {
    u8 byte, bit;
  } t[] =
  {
#define _(a,b,f) { .byte = a, .bit = b, },
    foreach_sfp_compatibility
#undef _
  };

  ASSERT (c < ARRAY_LEN (t));
  return (e->compatibility[t[c].byte] & (1 << t[c].bit)) != 0;
}

u8 *
format_sfp_eeprom (u8 * s, va_list * args)
{
  sfp_eeprom_t *e = va_arg (*args, sfp_eeprom_t *);
  u32 indent = format_get_indent (s);
  int i;

  if (e->id != SFP_ID_sfp)
    s = format (s, "id %U, ", format_sfp_id, e->id);

  s = format (s, "compatibility:");
  for (i = 0; i < SFP_N_COMPATIBILITY; i++)
    if (sfp_is_comatible (e, i))
      s = format (s, " %U", format_sfp_compatibility, i);

  s = format (s, "\n%Uvendor: %U, part %U",
	      format_white_space, indent,
	      format_space_terminated, sizeof (e->vendor_name),
	      e->vendor_name, format_space_terminated,
	      sizeof (e->vendor_part_number), e->vendor_part_number);
  s =
    format (s, "\n%Urevision: %U, serial: %U, date code: %U",
	    format_white_space, indent, format_space_terminated,
	    sizeof (e->vendor_revision), e->vendor_revision,
	    format_space_terminated, sizeof (e->vendor_serial_number),
	    e->vendor_serial_number, format_space_terminated,
	    sizeof (e->vendor_date_code), e->vendor_date_code);

  return s;
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
n class="n">clib_maplog_header_t _h, *h = &_h; ASSERT (a && a->mm); mm = a->mm; /* Already initialized? */ if (mm->flags & CLIB_MAPLOG_FLAG_INIT) return (-2); clib_memset (mm, 0, sizeof (*mm)); record_size_in_cache_lines = (a->record_size_in_bytes + CLIB_CACHE_LINE_BYTES - 1) / CLIB_CACHE_LINE_BYTES; file_size_in_records = a->file_size_in_bytes / (record_size_in_cache_lines * CLIB_CACHE_LINE_BYTES); /* Round up file size in records to a power of 2, for speed... */ mm->log2_file_size_in_records = max_log2 (file_size_in_records); file_size_in_records = 1ULL << (mm->log2_file_size_in_records); a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines * CLIB_CACHE_LINE_BYTES; mm->file_basename = format (0, "%s", a->file_basename); if (vec_len (mm->file_basename) > ARRAY_LEN (h->file_basename)) { vec_free (mm->file_basename); return -11; } mm->file_size_in_records = file_size_in_records; mm->flags |= CLIB_MAPLOG_FLAG_INIT; mm->record_size_in_cachelines = record_size_in_cache_lines; limit = 2; if (a->maplog_is_circular) { mm->log2_file_size_in_records = 63; mm->flags |= CLIB_MAPLOG_FLAG_CIRCULAR; limit = 1; } /* * Map the one and only file for a circular log, * two files for a normal log. */ for (i = 0; i < limit; i++) { mm->filenames[i] = format (0, "%v_%d", mm->file_basename, mm->current_file_index++); vec_add1 (mm->filenames[i], 0); fd = open ((char *) mm->filenames[i], O_CREAT | O_RDWR | O_TRUNC, 0600); if (fd < 0) { rv = -3; goto fail; } if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1) { rv = -4; goto fail; } if (write (fd, &zero, 1) != 1) { rv = -5; goto fail; } mm->file_baseva[i] = mmap (0, a->file_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mm->file_baseva[i] == (u8 *) MAP_FAILED) { clib_unix_warning ("mmap"); goto fail; } (void) close (fd); } clib_memset (h, 0, sizeof (*h)); h->maplog_major_version = MAPLOG_MAJOR_VERSION; h->maplog_minor_version = MAPLOG_MINOR_VERSION; h->maplog_patch_version = MAPLOG_PATCH_VERSION; h->application_id = a->application_id; h->application_major_version = a->application_major_version; h->application_minor_version = a->application_minor_version; h->application_patch_version = a->application_patch_version; h->record_size_in_cachelines = record_size_in_cache_lines; h->cacheline_size = CLIB_CACHE_LINE_BYTES; h->file_size_in_records = file_size_in_records; h->number_of_records = ~0ULL; h->number_of_files = ~0ULL; h->maplog_flag_circular = a->maplog_is_circular; memcpy (h->file_basename, mm->file_basename, vec_len (mm->file_basename)); mm->header_filename = format (0, "%v_header", mm->file_basename); vec_add1 (mm->header_filename, 0); fd = open ((char *) mm->header_filename, O_CREAT | O_RDWR | O_TRUNC, 0600); if (fd < 0) { clib_unix_warning ("header create"); rv = -6; goto fail; } rv = write (fd, h, sizeof (*h)); if (rv != sizeof (*h)) { clib_unix_warning ("header write"); rv = -7; goto fail; } (void) close (fd); return 0; fail: if (fd >= 0) (void) close (fd); for (i = 0; i < limit; i++) { if (mm->file_baseva[i]) (void) munmap ((u8 *) mm->file_baseva[i], a->file_size_in_bytes); if (mm->filenames[i]) (void) unlink ((char *) mm->filenames[i]); vec_free (mm->filenames[i]); } if (mm->header_filename) { (void) unlink ((char *) mm->header_filename); vec_free (mm->header_filename); } return rv; } /* slow path: unmap a full log segment, and replace it */ u8 * _clib_maplog_get_entry_slowpath (clib_maplog_main_t * mm, u64 my_record_index) { int fd; u8 *rv; u8 zero = 0; u32 unmap_index = (mm->current_file_index) & 1; u64 file_size_in_bytes = mm->file_size_in_records * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; /* This should never happen */ ASSERT ((mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) == 0); /* * Kill some time by calling format before we make the previous log * segment disappear. Obviously it won't do to call clib_maplog_get_entry(), * wait 100ms, and then fill in the log entry. */ vec_reset_length (mm->filenames[unmap_index]); mm->filenames[unmap_index] = format (mm->filenames[unmap_index], "%v_%d", mm->file_basename, mm->current_file_index++); /* Unmap the previous (full) segment */ (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes); /* Create a new segment */ fd = open ((char *) mm->filenames[unmap_index], O_CREAT | O_RDWR | O_TRUNC, 0600); /* This is not real error recovery... */ if (fd < 0) { clib_unix_warning ("creat"); abort (); } if (lseek (fd, file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1) { clib_unix_warning ("lseek"); abort (); } if (write (fd, &zero, 1) != 1) { clib_unix_warning ("set-size write"); abort (); } mm->file_baseva[unmap_index] = mmap (0, file_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mm->file_baseva[unmap_index] == (u8 *) MAP_FAILED) { clib_unix_warning ("mmap"); abort (); } (void) close (fd); rv = (u8 *) mm->file_baseva[(my_record_index >> mm->log2_file_size_in_records) & 1] + (my_record_index & (mm->file_size_in_records - 1)) * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; return rv; } /** * @brief Update a mapped log header file * * Read the log header. Update the number of records, and number of files * @param[in/out] mm mapped log object */ void clib_maplog_update_header (clib_maplog_main_t * mm) { int fd, rv; clib_maplog_header_t _h, *h = &_h; if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT)) return; /* Open the log header */ fd = open ((char *) mm->header_filename, O_RDWR, 0600); if (fd < 0) { clib_unix_warning ("reopen maplog header"); goto out; } /* Read the log */ rv = read (fd, h, sizeof (*h)); if (rv != sizeof (*h)) { clib_unix_warning ("read maplog header"); goto out; } /* Fix the header... */ h->number_of_records = mm->next_record_index; h->number_of_files = mm->current_file_index; h->maplog_flag_wrapped = (mm->flags & CLIB_MAPLOG_FLAG_WRAPPED) ? 1 : 0; /* Back to the beginning of the log header... */ if (lseek (fd, 0, SEEK_SET) < 0) { clib_unix_warning ("lseek to rewrite header"); goto out; } /* Rewrite the log header */ rv = write (fd, h, sizeof (*h)); if (rv != sizeof (*h)) clib_unix_warning ("rewrite header"); out: if (fd >= 0) (void) close (fd); } /** * @brief Close a mapped log, and update the log header file * * Unmap the current log segments. * Read the log header. Update the number of records, and number of files * * @param[in/out] mm mapped log object */ void clib_maplog_close (clib_maplog_main_t * mm) { int i, limit; u64 file_size_in_bytes; if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT)) return; clib_maplog_update_header (mm); file_size_in_bytes = mm->file_size_in_records * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; limit = (mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) ? 1 : 2; /* unmap current + next segments */ for (i = 0; i < limit; i++) { (void) munmap ((u8 *) mm->file_baseva[i], file_size_in_bytes); vec_free (mm->filenames[i]); } vec_free (mm->file_basename); vec_free (mm->header_filename); clib_memset (mm, 0, sizeof (*mm)); } /** * @brief format a log header * * Usage: s = format (0, "%U", format_maplog_header, headerp, verbose); * @param [in] h clib_maplog_header_t pointer * @param [in] verbose self-explanatory */ u8 * format_maplog_header (u8 * s, va_list * args) { clib_maplog_header_t *h = va_arg (*args, clib_maplog_header_t *); int verbose = va_arg (*args, int); if (!verbose) goto brief; s = format (s, "basename %s ", h->file_basename); s = format (s, "log ver %d.%d.%d app id %u ver %d.%d.%d %s %s\n", h->maplog_major_version, h->maplog_minor_version, h->maplog_patch_version, h->application_id, h->application_major_version, h->application_minor_version, h->application_patch_version, h->maplog_flag_circular ? "circular" : "linear", h->maplog_flag_wrapped ? "wrapped" : "not wrapped"); s = format (s, " records are %d %d-byte cachelines\n", h->record_size_in_cachelines, h->cacheline_size); s = format (s, " files are %lld records long, %lld files\n", h->file_size_in_records, h->number_of_files); s = format (s, " %lld records total\n", h->number_of_records); return s; brief: s = format (s, "%s %lld records %lld files %lld records/file", h->file_basename, h->number_of_records, h->number_of_files, h->file_size_in_records); return s; } /** * @brief Process a complete maplog * * Reads the maplog header. Map and process all log segments in order. * Calls the callback function once per file with a record count. * * Note: if the file header isn't updated by calling * clib_maplog_close(), it will appear to have an infinite * number of records in an infinite number of files. * * So long as the callback function understands that possibility * - by simply ignoring NULL records - the scheme still * works... * * @param [in] file_basename Same basename supplied to clib_maplog_init * @param [in] fp_arg Callback function pointer */ int clib_maplog_process (char *file_basename, void *fp_arg) { clib_maplog_header_t _h, *h = &_h; int fd, rv = 0; u64 file_index; u64 file_size_in_bytes; u8 *header_filename, *this_filename = 0; u8 *file_baseva; int (*fp) (clib_maplog_header_t *, void *data, u64 count); u64 records_this_file, records_left; ASSERT (fp_arg); fp = fp_arg; header_filename = format (0, "%s_header%c", file_basename, 0); fd = open ((char *) header_filename, O_RDONLY, 0600); if (fd < 0) { clib_unix_warning ("open maplog header"); rv = -1; goto out; } rv = read (fd, h, sizeof (*h)); if (rv != sizeof (*h)) { clib_unix_warning ("read maplog header"); rv = -2; goto out; } (void) close (fd); fd = -1; file_size_in_bytes = h->file_size_in_records * h->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; records_left = h->number_of_records; for (file_index = 0; file_index < h->number_of_files; file_index++) { vec_reset_length (this_filename); this_filename = format (this_filename, "%s_%llu%c", file_basename, file_index, 0); fd = open ((char *) this_filename, O_RDONLY, 0600); if (fd < 0) { rv = -3; goto out; } file_baseva = mmap (0, file_size_in_bytes, PROT_READ, MAP_SHARED, fd, 0); (void) close (fd); fd = -1; if (file_baseva == (u8 *) MAP_FAILED) { clib_unix_warning ("mmap"); rv = -4; goto out; } records_this_file = (records_left > h->file_size_in_records) ? h->file_size_in_records : records_left; /* * Normal log, or a circular non-wrapped log, or a circular * wrapped log which happens to be exactly linear */ if (h->maplog_flag_circular == 0 || h->maplog_flag_wrapped == 0 || ((h->number_of_records % h->file_size_in_records) == 0)) (*fp) (h, file_baseva, records_this_file); else { /* "Normal" wrapped circular log */ u64 first_chunk_record_index = h->number_of_records & (h->file_size_in_records - 1); u64 first_chunk_number_of_records = records_this_file - first_chunk_record_index; u8 *chunk_baseva = file_baseva + first_chunk_record_index * h->record_size_in_cachelines * h->cacheline_size; (*fp) (h, chunk_baseva, first_chunk_number_of_records); (*fp) (h, file_baseva, records_this_file - first_chunk_number_of_records); } if (munmap (file_baseva, file_size_in_bytes) < 0) { clib_warning ("munmap"); rv = -5; /* but don't stop... */ } records_left -= records_this_file; if (records_left == 0) break; } out: if (fd >= 0) (void) close (fd); vec_free (this_filename); vec_free (header_filename); return rv; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */