From 9593711c17e842d219f0853d8684e5c2b94d5d46 Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 11 Mar 2004 04:29:11 +0000 Subject: [PATCH] Merge HEAD to b1_2. b=2306, 2325, 2264 Fixes for supporting 8k llog chunks: - fix destroying of named logs (2325) - overwrite old logs when running lconf --write_conf (2264) - bump LLOG_CHUNKSIZE to 8k to allow for larger clusters (2306) --- lnet/include/lnet/types.h | 2 +- lustre/ChangeLog | 3 +++ lustre/include/linux/lustre_idl.h | 2 +- lustre/include/linux/lustre_lib.h | 1 + lustre/include/linux/obd_class.h | 2 +- lustre/mds/mds_fs.c | 6 ++--- lustre/mds/mds_lov.c | 27 ++++++++++++++++++++++- lustre/obdclass/llog_ioctl.c | 25 ++++++++++++++++----- lustre/obdclass/llog_lvfs.c | 34 ++++++++++++++++++++++++++++- lustre/obdclass/llog_test.c | 40 ++++++++++++++++++++++++++++++++++ lustre/portals/include/portals/types.h | 2 +- lustre/ptlrpc/llog_client.c | 16 ++++++++++++++ lustre/ptlrpc/pack_generic.c | 6 ++--- lustre/tests/.RC_CURRENT.tag | 2 +- lustre/utils/lconf | 12 ++++++++++ lustre/utils/lctl.c | 2 ++ lustre/utils/obd.c | 23 +++++++++++++++++++ lustre/utils/obdctl.h | 1 + lustre/utils/wiretest.c | 6 ++--- 19 files changed, 191 insertions(+), 21 deletions(-) diff --git a/lnet/include/lnet/types.h b/lnet/include/lnet/types.h index d4ca453..74ef493 100644 --- a/lnet/include/lnet/types.h +++ b/lnet/include/lnet/types.h @@ -135,7 +135,7 @@ typedef struct { struct timeval arrival_time; volatile ptl_seq_t sequence; -} __attribute__((packed)) ptl_event_t; +} ptl_event_t; #ifdef __CYGWIN__ #pragma pop #endif diff --git a/lustre/ChangeLog b/lustre/ChangeLog index a3d29e6..3d74556 100644 --- a/lustre/ChangeLog +++ b/lustre/ChangeLog @@ -7,6 +7,9 @@ tbd Cluster File Systems, Inc. - del obd_self_export from work_list in class_disconnect_exports (2908) - don't LBUG if MDS recovery times out during orphan cleanup (2530) - swab reply message in mdc_close, other PPC fixes (2464) + - fix destroying of named logs (2325) + - overwrite old logs when running lconf --write_conf (2264) + - bump LLOG_CHUNKSIZE to 8k to allow for larger clusters (2306) 2004-03-04 Cluster File Systems, Inc. * version 1.2.0 diff --git a/lustre/include/linux/lustre_idl.h b/lustre/include/linux/lustre_idl.h index c0452ea..72804d1 100644 --- a/lustre/include/linux/lustre_idl.h +++ b/lustre/include/linux/lustre_idl.h @@ -955,7 +955,7 @@ struct llog_gen_rec { struct llog_rec_tail lgr_tail; }; /* On-disk header structure of each log object, stored in little endian order */ -#define LLOG_CHUNK_SIZE 4096 +#define LLOG_CHUNK_SIZE 8192 #define LLOG_HEADER_SIZE (96) #define LLOG_BITMAP_BYTES (LLOG_CHUNK_SIZE - LLOG_HEADER_SIZE) diff --git a/lustre/include/linux/lustre_lib.h b/lustre/include/linux/lustre_lib.h index 4eef3be..24ad8fb 100644 --- a/lustre/include/linux/lustre_lib.h +++ b/lustre/include/linux/lustre_lib.h @@ -441,6 +441,7 @@ static inline void obd_ioctl_freedata(char *buf, int len) #define OBD_IOC_DORECORD _IOWR('f', 183, long) #define OBD_IOC_PROCESS_CFG _IOWR('f', 184, long) #define OBD_IOC_DUMP_LOG _IOWR('f', 185, long) +#define OBD_IOC_CLEAR_LOG _IOWR('f', 186, long) #define OBD_IOC_CATLOGLIST _IOWR('f', 190, long) #define OBD_IOC_LLOG_INFO _IOWR('f', 191, long) diff --git a/lustre/include/linux/obd_class.h b/lustre/include/linux/obd_class.h index 6c97a05..c7848b3 100644 --- a/lustre/include/linux/obd_class.h +++ b/lustre/include/linux/obd_class.h @@ -44,7 +44,7 @@ #include /* OBD Device Declarations */ -#define MAX_OBD_DEVICES 128 +#define MAX_OBD_DEVICES 256 extern struct obd_device obd_dev[MAX_OBD_DEVICES]; /* OBD Operations Declarations */ diff --git a/lustre/mds/mds_fs.c b/lustre/mds/mds_fs.c index d3e235a..6c69bd4 100644 --- a/lustre/mds/mds_fs.c +++ b/lustre/mds/mds_fs.c @@ -643,7 +643,7 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa, de = lookup_one_len(fidname, mds->mds_objects_dir, namelen); if (de == NULL || de->d_inode == NULL) { CERROR("destroying non-existent object "LPU64"\n", oa->o_id); - GOTO(out, rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT); + GOTO(out_dput, rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT); } handle = fsfilt_start(obd, mds->mds_objects_dir->d_inode, @@ -661,8 +661,8 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa, if (err && !rc) rc = err; out_dput: - l_dput(de); -out: + if (de != NULL) + l_dput(de); up(&parent_inode->i_sem); pop_ctxt(&saved, &obd->obd_ctxt, NULL); RETURN(rc); diff --git a/lustre/mds/mds_lov.c b/lustre/mds/mds_lov.c index 3520849..c6b6839 100644 --- a/lustre/mds/mds_lov.c +++ b/lustre/mds/mds_lov.c @@ -364,6 +364,27 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, RETURN(rc); } + case OBD_IOC_CLEAR_LOG: { + char *name = data->ioc_inlbuf1; + if (mds->mds_cfg_llh) + RETURN(-EBUSY); + + push_ctxt(&saved, &obd->obd_ctxt, NULL); + rc = llog_create(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT), + &mds->mds_cfg_llh, NULL, name); + if (rc == 0) { + llog_init_handle(mds->mds_cfg_llh, LLOG_F_IS_PLAIN, + NULL); + + rc = llog_destroy(mds->mds_cfg_llh); + llog_free_handle(mds->mds_cfg_llh); + } + pop_ctxt(&saved, &obd->obd_ctxt, NULL); + + mds->mds_cfg_llh = NULL; + RETURN(rc); + } + case OBD_IOC_DORECORD: { char *cfg_buf; struct llog_rec_hdr rec; @@ -449,13 +470,17 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, case OBD_IOC_LLOG_REMOVE: { struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT); + int rc2; obd_llog_finish(obd, mds->mds_lov_desc.ld_tgt_count); push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); rc = llog_ioctl(ctxt, cmd, data); pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); llog_cat_initialize(obd, mds->mds_lov_desc.ld_tgt_count); - + rc2 = obd_set_info(mds->mds_osc_exp, strlen("mds_conn"), "mds_conn", + 0, NULL); + if (!rc) + rc = rc2; RETURN(rc); } case OBD_IOC_LLOG_INFO: diff --git a/lustre/obdclass/llog_ioctl.c b/lustre/obdclass/llog_ioctl.c index 14d20f2..6c060e7 100644 --- a/lustre/obdclass/llog_ioctl.c +++ b/lustre/obdclass/llog_ioctl.c @@ -34,7 +34,7 @@ static int str2logid(struct llog_logid *logid, char *str, int len) RETURN(-EINVAL); *end = '\0'; - logid->lgl_oid = simple_strtoull(start, &endp, 16); + logid->lgl_oid = simple_strtoull(start, &endp, 0); if (endp != end) RETURN(-EINVAL); @@ -46,7 +46,7 @@ static int str2logid(struct llog_logid *logid, char *str, int len) RETURN(-EINVAL); *end = '\0'; - logid->lgl_ogr = simple_strtoull(start, &endp, 16); + logid->lgl_ogr = simple_strtoull(start, &endp, 0); if (endp != end) RETURN(-EINVAL); @@ -316,15 +316,23 @@ int llog_ioctl(struct llog_ctxt *ctxt, int cmd, struct obd_ioctl_data *data) struct llog_logid plain; char *endp; - if (!(handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)) + cookie.lgc_index = simple_strtoul(data->ioc_inlbuf3, &endp, 0); + if (*endp != '\0') GOTO(out_close, err = -EINVAL); + if (handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT) { + down_write(&handle->lgh_lock); + err = llog_cancel_rec(handle, cookie.lgc_index); + up_write(&handle->lgh_lock); + GOTO(out_close, err); + } + err = str2logid(&plain, data->ioc_inlbuf2, data->ioc_inllen2); if (err) GOTO(out_close, err); cookie.lgc_lgl = plain; - cookie.lgc_index = simple_strtoul(data->ioc_inlbuf3, &endp, 0); - if (*endp != '\0') + + if (!(handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)) GOTO(out_close, err = -EINVAL); err = llog_cat_cancel_records(handle, 1, &cookie); @@ -333,6 +341,13 @@ int llog_ioctl(struct llog_ctxt *ctxt, int cmd, struct obd_ioctl_data *data) case OBD_IOC_LLOG_REMOVE: { struct llog_logid plain; + if (handle->lgh_hdr->llh_flags & cpu_to_le32(LLOG_F_IS_PLAIN)) { + err = llog_destroy(handle); + if (!err) + llog_free_handle(handle); + GOTO(out, err); + } + if (!(handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)) GOTO(out_close, err = -EINVAL); diff --git a/lustre/obdclass/llog_lvfs.c b/lustre/obdclass/llog_lvfs.c index ad0b562..168a004 100644 --- a/lustre/obdclass/llog_lvfs.c +++ b/lustre/obdclass/llog_lvfs.c @@ -162,8 +162,26 @@ static int llog_lvfs_read_header(struct llog_handle *handle) rc = llog_lvfs_read_blob(obd, handle->lgh_file, handle->lgh_hdr, LLOG_CHUNK_SIZE, 0); - if (rc) + if (rc) { CERROR("error reading log header\n"); + } else { + struct llog_rec_hdr *llh_hdr = &handle->lgh_hdr->llh_hdr; + /* + * These need to be fixed for bug 1987 + */ + if (llh_hdr->lrh_type != LLOG_HDR_MAGIC) { + CERROR("bad log header magic: %#x (expecting %#x)\n", + llh_hdr->lrh_type, LLOG_HDR_MAGIC); + rc = -EIO; + } else if (llh_hdr->lrh_len != LLOG_CHUNK_SIZE) { + CERROR("incorrectly sized log header: %#x " + "(expecting %#x)\n", + llh_hdr->lrh_len, LLOG_CHUNK_SIZE); + CERROR("you may need to re-run lconf --write_conf.\n"); + rc = -EIO; + } + } + handle->lgh_last_idx = handle->lgh_hdr->llh_tail.lrt_index; handle->lgh_file->f_pos = handle->lgh_file->f_dentry->d_inode->i_size; @@ -532,10 +550,24 @@ static int llog_lvfs_close(struct llog_handle *handle) static int llog_lvfs_destroy(struct llog_handle *handle) { + struct dentry *fdentry; struct obdo *oa; int rc; ENTRY; + fdentry = handle->lgh_file->f_dentry; + if (!strcmp(fdentry->d_parent->d_name.name, "LOGS")) { + struct inode *inode = fdentry->d_parent->d_inode; + rc = llog_lvfs_close(handle); + if (rc) + RETURN(rc); + + down(&inode->i_sem); + rc = vfs_unlink(inode, fdentry); + up(&inode->i_sem); + RETURN(rc); + } + oa = obdo_alloc(); if (oa == NULL) RETURN(-ENOMEM); diff --git a/lustre/obdclass/llog_test.c b/lustre/obdclass/llog_test.c index f8e6de1..e3edd02 100644 --- a/lustre/obdclass/llog_test.c +++ b/lustre/obdclass/llog_test.c @@ -489,6 +489,42 @@ parse_out: RETURN(rc); } +static int llog_test_7(struct obd_device *obd) +{ + struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); + struct llog_handle *llh; + struct llog_create_rec lcr; + char name[10]; + int rc; + ENTRY; + + sprintf(name, "%x", llog_test_rand+2); + CWARN("7: create a log with name: %s\n", name); + LASSERT(ctxt); + + rc = llog_create(ctxt, &llh, NULL, name); + if (rc) { + CERROR("7: llog_create with name %s failed: %d\n", name, rc); + RETURN(rc); + } + llog_init_handle(llh, LLOG_F_IS_PLAIN, &uuid); + + lcr.lcr_hdr.lrh_len = lcr.lcr_tail.lrt_len = cpu_to_le32(sizeof(lcr)); + lcr.lcr_hdr.lrh_type = cpu_to_le32(OST_SZ_REC); + rc = llog_write_rec(llh, &lcr.lcr_hdr, NULL, 0, NULL, -1); + if (rc) { + CERROR("7: write one log record failed: %d\n", rc); + RETURN(rc); + } + + rc = llog_destroy(llh); + if (rc) + CERROR("7: llog_destroy failed: %d\n", rc); + else + llog_free_handle(llh); + RETURN(rc); +} + /* ------------------------------------------------------------------------- * Tests above, boring obd functions below * ------------------------------------------------------------------------- */ @@ -529,6 +565,10 @@ static int llog_run_tests(struct obd_device *obd) if (rc) GOTO(cleanup, rc); + rc = llog_test_7(obd); + if (rc) + GOTO(cleanup, rc); + cleanup: switch (cleanup_phase) { case 1: diff --git a/lustre/portals/include/portals/types.h b/lustre/portals/include/portals/types.h index d4ca453..74ef493 100644 --- a/lustre/portals/include/portals/types.h +++ b/lustre/portals/include/portals/types.h @@ -135,7 +135,7 @@ typedef struct { struct timeval arrival_time; volatile ptl_seq_t sequence; -} __attribute__((packed)) ptl_event_t; +} ptl_event_t; #ifdef __CYGWIN__ #pragma pop #endif diff --git a/lustre/ptlrpc/llog_client.c b/lustre/ptlrpc/llog_client.c index d34e5e2..1098b40 100644 --- a/lustre/ptlrpc/llog_client.c +++ b/lustre/ptlrpc/llog_client.c @@ -169,6 +169,7 @@ static int llog_client_read_header(struct llog_handle *handle) struct ptlrpc_request *req = NULL; struct llogd_body *body; struct llog_log_hdr *hdr; + struct llog_rec_hdr *llh_hdr; int size = sizeof(*body); int repsize = sizeof (*hdr); int rc; @@ -193,9 +194,24 @@ static int llog_client_read_header(struct llog_handle *handle) CERROR ("Can't unpack llog_hdr\n"); GOTO(out, rc =-EFAULT); } + memcpy(handle->lgh_hdr, hdr, sizeof (*hdr)); handle->lgh_last_idx = handle->lgh_hdr->llh_tail.lrt_index; + /* sanity checks */ + llh_hdr = &handle->lgh_hdr->llh_hdr; + if (llh_hdr->lrh_type != LLOG_HDR_MAGIC) { + CERROR("bad log header magic: %#x (expecting %#x)\n", + llh_hdr->lrh_type, LLOG_HDR_MAGIC); + rc = -EIO; + } else if (llh_hdr->lrh_len != LLOG_CHUNK_SIZE) { + CERROR("incorrectly sized log header: %#x " + "(expecting %#x)\n", + llh_hdr->lrh_len, LLOG_CHUNK_SIZE); + CERROR("you may need to re-run lconf --write_conf.\n"); + rc = -EIO; + } + out: if (req) ptlrpc_req_finished(req); diff --git a/lustre/ptlrpc/pack_generic.c b/lustre/ptlrpc/pack_generic.c index f34f5f2..f424e53 100644 --- a/lustre/ptlrpc/pack_generic.c +++ b/lustre/ptlrpc/pack_generic.c @@ -2011,7 +2011,7 @@ void lustre_assert_wire_constants(void) (long long)(int)sizeof(((struct llog_gen_rec *)0)->lgr_tail)); /* Checks for struct llog_log_hdr */ - LASSERTF((int)sizeof(struct llog_log_hdr) == 4096, " found %lld\n", + LASSERTF((int)sizeof(struct llog_log_hdr) == 8192, " found %lld\n", (long long)(int)sizeof(struct llog_log_hdr)); LASSERTF(offsetof(struct llog_log_hdr, llh_hdr) == 0, " found %lld\n", (long long)offsetof(struct llog_log_hdr, llh_hdr)); @@ -2051,9 +2051,9 @@ void lustre_assert_wire_constants(void) (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_reserved)); LASSERTF(offsetof(struct llog_log_hdr, llh_bitmap) == 88, " found %lld\n", (long long)offsetof(struct llog_log_hdr, llh_bitmap)); - LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap) == 4000, " found %lld\n", + LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap) == 8096, " found %lld\n", (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap)); - LASSERTF(offsetof(struct llog_log_hdr, llh_tail) == 4088, " found %lld\n", + LASSERTF(offsetof(struct llog_log_hdr, llh_tail) == 8184, " found %lld\n", (long long)offsetof(struct llog_log_hdr, llh_tail)); LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_tail) == 8, " found %lld\n", (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_tail)); diff --git a/lustre/tests/.RC_CURRENT.tag b/lustre/tests/.RC_CURRENT.tag index 0989b52..9cb6c4a 100644 --- a/lustre/tests/.RC_CURRENT.tag +++ b/lustre/tests/.RC_CURRENT.tag @@ -1 +1 @@ -RC_1_3_0_1 +RC_1_3_0_2 diff --git a/lustre/utils/lconf b/lustre/utils/lconf index 30f8437..322e1f1 100755 --- a/lustre/utils/lconf +++ b/lustre/utils/lconf @@ -399,6 +399,15 @@ class LCTLInterface: return rc, out + def clear_log(self, dev, log): + """ clear an existing log """ + cmds = """ + device $%s + probe + clear_log %s + quit """ % (dev, log) + self.run(cmds) + def network(self, net, nid): """ set mynid """ cmds = """ @@ -1459,12 +1468,14 @@ class MDSDEV(Module): client = VOSC(self.db.lookup(obd_uuid), client_uuid, self.name, self.name) config.record = 1 + lctl.clear_log(self.name, self.name) lctl.record(self.name, self.name) client.prepare() lctl.mount_option(self.name, client.get_name(), "") lctl.end_record() config.cleanup = 1 + lctl.clear_log(self.name, self.name + '-clean') lctl.record(self.name, self.name + '-clean') client.cleanup() lctl.del_mount_option(self.name) @@ -2656,6 +2667,7 @@ def main(): if config.record: if not (config.record_device and config.record_log): panic("When recording, both --record_log and --record_device must be specified.") + lctl.clear_log(config.record_device, config.record_log) lctl.record(config.record_device, config.record_log) doHost(db, node_list) diff --git a/lustre/utils/lctl.c b/lustre/utils/lctl.c index c9d69aa..b1c8ca5 100644 --- a/lustre/utils/lctl.c +++ b/lustre/utils/lctl.c @@ -164,6 +164,8 @@ command_t cmdlist[] = { "usage: parse config-uuid-name"}, {"dump_log", jt_cfg_dump_log, 0, "print log of recorded commands for this config to kernel debug log\n" "usage: dump_log config-uuid-name"}, + {"clear_log", jt_cfg_clear_log, 0, "delete current config log of recorded commands\n" + "usage: clear_log config-name"}, /* Device operations */ {"=== device operations ==", jt_noop, 0, "device operations"}, diff --git a/lustre/utils/obd.c b/lustre/utils/obd.c index e4ef0a8..3a4089a 100644 --- a/lustre/utils/obd.c +++ b/lustre/utils/obd.c @@ -1765,6 +1765,29 @@ int jt_cfg_dump_log(int argc, char **argv) return rc; } +int jt_cfg_clear_log(int argc, char **argv) +{ + struct obd_ioctl_data data; + int rc; + + IOC_INIT(data); + + if (argc != 2) + return CMD_HELP; + + data.ioc_inllen1 = strlen(argv[1]) + 1; + data.ioc_inlbuf1 = argv[1]; + + IOC_PACK(argv[0], data); + rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CLEAR_LOG, buf); + if (rc < 0) + fprintf(stderr, "OBD_IOC_CLEAR_LOG failed: %s\n", + strerror(errno)); + + return rc; +} + + int jt_cfg_endrecord(int argc, char **argv) { diff --git a/lustre/utils/obdctl.h b/lustre/utils/obdctl.h index b813795..15067d8 100644 --- a/lustre/utils/obdctl.h +++ b/lustre/utils/obdctl.h @@ -78,6 +78,7 @@ int jt_cfg_record(int argc, char **argv); int jt_cfg_endrecord(int argc, char **argv); int jt_cfg_parse(int argc, char **argv); int jt_cfg_dump_log(int argc, char **argv); +int jt_cfg_clear_log(int argc, char **argv); int jt_llog_catlist(int argc, char **argv); int jt_llog_info(int argc, char **argv); diff --git a/lustre/utils/wiretest.c b/lustre/utils/wiretest.c index 849b0f0..ed317f9 100644 --- a/lustre/utils/wiretest.c +++ b/lustre/utils/wiretest.c @@ -1368,7 +1368,7 @@ void lustre_assert_wire_constants(void) (long long)(int)sizeof(((struct llog_gen_rec *)0)->lgr_tail)); /* Checks for struct llog_log_hdr */ - LASSERTF((int)sizeof(struct llog_log_hdr) == 4096, " found %lld\n", + LASSERTF((int)sizeof(struct llog_log_hdr) == 8192, " found %lld\n", (long long)(int)sizeof(struct llog_log_hdr)); LASSERTF(offsetof(struct llog_log_hdr, llh_hdr) == 0, " found %lld\n", (long long)offsetof(struct llog_log_hdr, llh_hdr)); @@ -1408,9 +1408,9 @@ void lustre_assert_wire_constants(void) (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_reserved)); LASSERTF(offsetof(struct llog_log_hdr, llh_bitmap) == 88, " found %lld\n", (long long)offsetof(struct llog_log_hdr, llh_bitmap)); - LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap) == 4000, " found %lld\n", + LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap) == 8096, " found %lld\n", (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap)); - LASSERTF(offsetof(struct llog_log_hdr, llh_tail) == 4088, " found %lld\n", + LASSERTF(offsetof(struct llog_log_hdr, llh_tail) == 8184, " found %lld\n", (long long)offsetof(struct llog_log_hdr, llh_tail)); LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_tail) == 8, " found %lld\n", (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_tail)); -- 1.8.3.1