| /* |
| * GPL HEADER START |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 only, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License version 2 for more details (a copy is included |
| * in the LICENSE file that accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License |
| * version 2 along with this program; If not, see |
| * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| * |
| * GPL HEADER END |
| */ |
| /* |
| * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright (c) 2011, 2012, Intel Corporation. |
| */ |
| /* |
| * This file is part of Lustre, http://www.lustre.org/ |
| * Lustre is a trademark of Sun Microsystems, Inc. |
| * |
| * lustre/obdclass/llog_lvfs.c |
| * |
| * OST<->MDS recovery logging infrastructure. |
| * Invariants in implementation: |
| * - we do not share logs among different OST<->MDS connections, so that |
| * if an OST or MDS fails it need only look at log(s) relevant to itself |
| * |
| * Author: Andreas Dilger <adilger@clusterfs.com> |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_LOG |
| |
| |
| #include <obd.h> |
| #include <obd_class.h> |
| #include <lustre_log.h> |
| #include <obd_ost.h> |
| #include <linux/list.h> |
| #include <lvfs.h> |
| #include <lustre_fsfilt.h> |
| #include <lustre_disk.h> |
| #include "llog_internal.h" |
| |
| #if defined(LLOG_LVFS) |
| |
| static int llog_lvfs_pad(struct obd_device *obd, struct l_file *file, |
| int len, int index) |
| { |
| struct llog_rec_hdr rec = { 0 }; |
| struct llog_rec_tail tail; |
| int rc; |
| |
| LASSERT(len >= LLOG_MIN_REC_SIZE && (len & 0x7) == 0); |
| |
| tail.lrt_len = rec.lrh_len = len; |
| tail.lrt_index = rec.lrh_index = index; |
| rec.lrh_type = LLOG_PAD_MAGIC; |
| |
| rc = fsfilt_write_record(obd, file, &rec, sizeof(rec), &file->f_pos, 0); |
| if (rc) { |
| CERROR("error writing padding record: rc %d\n", rc); |
| goto out; |
| } |
| |
| file->f_pos += len - sizeof(rec) - sizeof(tail); |
| rc = fsfilt_write_record(obd, file, &tail, sizeof(tail),&file->f_pos,0); |
| if (rc) { |
| CERROR("error writing padding record: rc %d\n", rc); |
| goto out; |
| } |
| |
| out: |
| return rc; |
| } |
| |
| static int llog_lvfs_write_blob(struct obd_device *obd, struct l_file *file, |
| struct llog_rec_hdr *rec, void *buf, loff_t off) |
| { |
| int rc; |
| struct llog_rec_tail end; |
| loff_t saved_off = file->f_pos; |
| int buflen = rec->lrh_len; |
| |
| file->f_pos = off; |
| |
| if (buflen == 0) |
| CWARN("0-length record\n"); |
| |
| if (!buf) { |
| rc = fsfilt_write_record(obd, file, rec, buflen,&file->f_pos,0); |
| if (rc) { |
| CERROR("error writing log record: rc %d\n", rc); |
| goto out; |
| } |
| GOTO(out, rc = 0); |
| } |
| |
| /* the buf case */ |
| rec->lrh_len = sizeof(*rec) + buflen + sizeof(end); |
| rc = fsfilt_write_record(obd, file, rec, sizeof(*rec), &file->f_pos, 0); |
| if (rc) { |
| CERROR("error writing log hdr: rc %d\n", rc); |
| goto out; |
| } |
| |
| rc = fsfilt_write_record(obd, file, buf, buflen, &file->f_pos, 0); |
| if (rc) { |
| CERROR("error writing log buffer: rc %d\n", rc); |
| goto out; |
| } |
| |
| end.lrt_len = rec->lrh_len; |
| end.lrt_index = rec->lrh_index; |
| rc = fsfilt_write_record(obd, file, &end, sizeof(end), &file->f_pos, 0); |
| if (rc) { |
| CERROR("error writing log tail: rc %d\n", rc); |
| goto out; |
| } |
| |
| rc = 0; |
| out: |
| if (saved_off > file->f_pos) |
| file->f_pos = saved_off; |
| LASSERT(rc <= 0); |
| return rc; |
| } |
| |
| static int llog_lvfs_read_blob(struct obd_device *obd, struct l_file *file, |
| void *buf, int size, loff_t off) |
| { |
| loff_t offset = off; |
| int rc; |
| |
| rc = fsfilt_read_record(obd, file, buf, size, &offset); |
| if (rc) { |
| CERROR("error reading log record: rc %d\n", rc); |
| return rc; |
| } |
| return 0; |
| } |
| |
| static int llog_lvfs_read_header(const struct lu_env *env, |
| struct llog_handle *handle) |
| { |
| struct obd_device *obd; |
| int rc; |
| |
| LASSERT(sizeof(*handle->lgh_hdr) == LLOG_CHUNK_SIZE); |
| |
| obd = handle->lgh_ctxt->loc_exp->exp_obd; |
| |
| if (i_size_read(handle->lgh_file->f_dentry->d_inode) == 0) { |
| CDEBUG(D_HA, "not reading header from 0-byte log\n"); |
| return LLOG_EEMPTY; |
| } |
| |
| rc = llog_lvfs_read_blob(obd, handle->lgh_file, handle->lgh_hdr, |
| LLOG_CHUNK_SIZE, 0); |
| if (rc) { |
| CERROR("error reading log header from %.*s\n", |
| handle->lgh_file->f_dentry->d_name.len, |
| handle->lgh_file->f_dentry->d_name.name); |
| } else { |
| struct llog_rec_hdr *llh_hdr = &handle->lgh_hdr->llh_hdr; |
| |
| if (LLOG_REC_HDR_NEEDS_SWABBING(llh_hdr)) |
| lustre_swab_llog_hdr(handle->lgh_hdr); |
| |
| if (llh_hdr->lrh_type != LLOG_HDR_MAGIC) { |
| CERROR("bad log %.*s header magic: %#x (expected %#x)\n", |
| handle->lgh_file->f_dentry->d_name.len, |
| handle->lgh_file->f_dentry->d_name.name, |
| llh_hdr->lrh_type, LLOG_HDR_MAGIC); |
| rc = -EIO; |
| } else if (llh_hdr->lrh_len != LLOG_CHUNK_SIZE) { |
| CERROR("incorrectly sized log %.*s header: %#x " |
| "(expected %#x)\n", |
| handle->lgh_file->f_dentry->d_name.len, |
| handle->lgh_file->f_dentry->d_name.name, |
| 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 = i_size_read(handle->lgh_file->f_dentry->d_inode); |
| |
| return rc; |
| } |
| |
| /* returns negative in on error; 0 if success && reccookie == 0; 1 otherwise */ |
| /* appends if idx == -1, otherwise overwrites record idx. */ |
| static int llog_lvfs_write_rec(const struct lu_env *env, |
| struct llog_handle *loghandle, |
| struct llog_rec_hdr *rec, |
| struct llog_cookie *reccookie, int cookiecount, |
| void *buf, int idx, struct thandle *th) |
| { |
| struct llog_log_hdr *llh; |
| int reclen = rec->lrh_len, index, rc; |
| struct llog_rec_tail *lrt; |
| struct obd_device *obd; |
| struct file *file; |
| size_t left; |
| |
| llh = loghandle->lgh_hdr; |
| file = loghandle->lgh_file; |
| obd = loghandle->lgh_ctxt->loc_exp->exp_obd; |
| |
| /* record length should not bigger than LLOG_CHUNK_SIZE */ |
| if (buf) |
| rc = (reclen > LLOG_CHUNK_SIZE - sizeof(struct llog_rec_hdr) - |
| sizeof(struct llog_rec_tail)) ? -E2BIG : 0; |
| else |
| rc = (reclen > LLOG_CHUNK_SIZE) ? -E2BIG : 0; |
| if (rc) |
| return rc; |
| |
| if (buf) |
| /* write_blob adds header and tail to lrh_len. */ |
| reclen = sizeof(*rec) + rec->lrh_len + |
| sizeof(struct llog_rec_tail); |
| |
| if (idx != -1) { |
| loff_t saved_offset; |
| |
| /* no header: only allowed to insert record 1 */ |
| if (idx != 1 && !i_size_read(file->f_dentry->d_inode)) { |
| CERROR("idx != -1 in empty log\n"); |
| LBUG(); |
| } |
| |
| if (idx && llh->llh_size && llh->llh_size != rec->lrh_len) |
| return -EINVAL; |
| |
| if (!ext2_test_bit(idx, llh->llh_bitmap)) |
| CERROR("Modify unset record %u\n", idx); |
| if (idx != rec->lrh_index) |
| CERROR("Index mismatch %d %u\n", idx, rec->lrh_index); |
| |
| rc = llog_lvfs_write_blob(obd, file, &llh->llh_hdr, NULL, 0); |
| /* we are done if we only write the header or on error */ |
| if (rc || idx == 0) |
| return rc; |
| |
| if (buf) { |
| /* We assume that caller has set lgh_cur_* */ |
| saved_offset = loghandle->lgh_cur_offset; |
| CDEBUG(D_OTHER, |
| "modify record "DOSTID": idx:%d/%u/%d, len:%u " |
| "offset %llu\n", |
| POSTID(&loghandle->lgh_id.lgl_oi), idx, rec->lrh_index, |
| loghandle->lgh_cur_idx, rec->lrh_len, |
| (long long)(saved_offset - sizeof(*llh))); |
| if (rec->lrh_index != loghandle->lgh_cur_idx) { |
| CERROR("modify idx mismatch %u/%d\n", |
| idx, loghandle->lgh_cur_idx); |
| return -EFAULT; |
| } |
| } else { |
| /* Assumes constant lrh_len */ |
| saved_offset = sizeof(*llh) + (idx - 1) * reclen; |
| } |
| |
| rc = llog_lvfs_write_blob(obd, file, rec, buf, saved_offset); |
| if (rc == 0 && reccookie) { |
| reccookie->lgc_lgl = loghandle->lgh_id; |
| reccookie->lgc_index = idx; |
| rc = 1; |
| } |
| return rc; |
| } |
| |
| /* Make sure that records don't cross a chunk boundary, so we can |
| * process them page-at-a-time if needed. If it will cross a chunk |
| * boundary, write in a fake (but referenced) entry to pad the chunk. |
| * |
| * We know that llog_current_log() will return a loghandle that is |
| * big enough to hold reclen, so all we care about is padding here. |
| */ |
| left = LLOG_CHUNK_SIZE - (file->f_pos & (LLOG_CHUNK_SIZE - 1)); |
| |
| /* NOTE: padding is a record, but no bit is set */ |
| if (left != 0 && left != reclen && |
| left < (reclen + LLOG_MIN_REC_SIZE)) { |
| index = loghandle->lgh_last_idx + 1; |
| rc = llog_lvfs_pad(obd, file, left, index); |
| if (rc) |
| return rc; |
| loghandle->lgh_last_idx++; /*for pad rec*/ |
| } |
| /* if it's the last idx in log file, then return -ENOSPC */ |
| if (loghandle->lgh_last_idx >= LLOG_BITMAP_SIZE(llh) - 1) |
| return -ENOSPC; |
| loghandle->lgh_last_idx++; |
| index = loghandle->lgh_last_idx; |
| LASSERT(index < LLOG_BITMAP_SIZE(llh)); |
| rec->lrh_index = index; |
| if (buf == NULL) { |
| lrt = (struct llog_rec_tail *) |
| ((char *)rec + rec->lrh_len - sizeof(*lrt)); |
| lrt->lrt_len = rec->lrh_len; |
| lrt->lrt_index = rec->lrh_index; |
| } |
| /*The caller should make sure only 1 process access the lgh_last_idx, |
| *Otherwise it might hit the assert.*/ |
| LASSERT(index < LLOG_BITMAP_SIZE(llh)); |
| spin_lock(&loghandle->lgh_hdr_lock); |
| if (ext2_set_bit(index, llh->llh_bitmap)) { |
| CERROR("argh, index %u already set in log bitmap?\n", index); |
| spin_unlock(&loghandle->lgh_hdr_lock); |
| LBUG(); /* should never happen */ |
| } |
| llh->llh_count++; |
| spin_unlock(&loghandle->lgh_hdr_lock); |
| llh->llh_tail.lrt_index = index; |
| |
| rc = llog_lvfs_write_blob(obd, file, &llh->llh_hdr, NULL, 0); |
| if (rc) |
| return rc; |
| |
| rc = llog_lvfs_write_blob(obd, file, rec, buf, file->f_pos); |
| if (rc) |
| return rc; |
| |
| CDEBUG(D_RPCTRACE, "added record "DOSTID": idx: %u, %u \n", |
| POSTID(&loghandle->lgh_id.lgl_oi), index, rec->lrh_len); |
| if (rc == 0 && reccookie) { |
| reccookie->lgc_lgl = loghandle->lgh_id; |
| reccookie->lgc_index = index; |
| if ((rec->lrh_type == MDS_UNLINK_REC) || |
| (rec->lrh_type == MDS_SETATTR64_REC)) |
| reccookie->lgc_subsys = LLOG_MDS_OST_ORIG_CTXT; |
| else if (rec->lrh_type == OST_SZ_REC) |
| reccookie->lgc_subsys = LLOG_SIZE_ORIG_CTXT; |
| else |
| reccookie->lgc_subsys = -1; |
| rc = 1; |
| } |
| if (rc == 0 && rec->lrh_type == LLOG_GEN_REC) |
| rc = 1; |
| |
| return rc; |
| } |
| |
| /* We can skip reading at least as many log blocks as the number of |
| * minimum sized log records we are skipping. If it turns out |
| * that we are not far enough along the log (because the |
| * actual records are larger than minimum size) we just skip |
| * some more records. */ |
| |
| static void llog_skip_over(__u64 *off, int curr, int goal) |
| { |
| if (goal <= curr) |
| return; |
| *off = (*off + (goal-curr-1) * LLOG_MIN_REC_SIZE) & |
| ~(LLOG_CHUNK_SIZE - 1); |
| } |
| |
| |
| /* sets: |
| * - cur_offset to the furthest point read in the log file |
| * - cur_idx to the log index preceeding cur_offset |
| * returns -EIO/-EINVAL on error |
| */ |
| static int llog_lvfs_next_block(const struct lu_env *env, |
| struct llog_handle *loghandle, int *cur_idx, |
| int next_idx, __u64 *cur_offset, void *buf, |
| int len) |
| { |
| int rc; |
| |
| if (len == 0 || len & (LLOG_CHUNK_SIZE - 1)) |
| return -EINVAL; |
| |
| CDEBUG(D_OTHER, "looking for log index %u (cur idx %u off "LPU64")\n", |
| next_idx, *cur_idx, *cur_offset); |
| |
| while (*cur_offset < i_size_read(loghandle->lgh_file->f_dentry->d_inode)) { |
| struct llog_rec_hdr *rec, *last_rec; |
| struct llog_rec_tail *tail; |
| loff_t ppos; |
| int llen; |
| |
| llog_skip_over(cur_offset, *cur_idx, next_idx); |
| |
| /* read up to next LLOG_CHUNK_SIZE block */ |
| ppos = *cur_offset; |
| llen = LLOG_CHUNK_SIZE - (*cur_offset & (LLOG_CHUNK_SIZE - 1)); |
| rc = fsfilt_read_record(loghandle->lgh_ctxt->loc_exp->exp_obd, |
| loghandle->lgh_file, buf, llen, |
| cur_offset); |
| if (rc < 0) { |
| CERROR("Cant read llog block at log id "DOSTID |
| "/%u offset "LPU64"\n", |
| POSTID(&loghandle->lgh_id.lgl_oi), |
| loghandle->lgh_id.lgl_ogen, |
| *cur_offset); |
| return rc; |
| } |
| |
| /* put number of bytes read into rc to make code simpler */ |
| rc = *cur_offset - ppos; |
| if (rc < len) { |
| /* signal the end of the valid buffer to llog_process */ |
| memset(buf + rc, 0, len - rc); |
| } |
| |
| if (rc == 0) /* end of file, nothing to do */ |
| return 0; |
| |
| if (rc < sizeof(*tail)) { |
| CERROR("Invalid llog block at log id "DOSTID"/%u offset" |
| LPU64"\n", POSTID(&loghandle->lgh_id.lgl_oi), |
| loghandle->lgh_id.lgl_ogen, *cur_offset); |
| return -EINVAL; |
| } |
| |
| rec = buf; |
| if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) |
| lustre_swab_llog_rec(rec); |
| |
| tail = (struct llog_rec_tail *)(buf + rc - |
| sizeof(struct llog_rec_tail)); |
| |
| /* get the last record in block */ |
| last_rec = (struct llog_rec_hdr *)(buf + rc - |
| le32_to_cpu(tail->lrt_len)); |
| |
| if (LLOG_REC_HDR_NEEDS_SWABBING(last_rec)) |
| lustre_swab_llog_rec(last_rec); |
| LASSERT(last_rec->lrh_index == tail->lrt_index); |
| |
| *cur_idx = tail->lrt_index; |
| |
| /* this shouldn't happen */ |
| if (tail->lrt_index == 0) { |
| CERROR("Invalid llog tail at log id "DOSTID"/%u offset " |
| LPU64"\n", POSTID(&loghandle->lgh_id.lgl_oi), |
| loghandle->lgh_id.lgl_ogen, *cur_offset); |
| return -EINVAL; |
| } |
| if (tail->lrt_index < next_idx) |
| continue; |
| |
| /* sanity check that the start of the new buffer is no farther |
| * than the record that we wanted. This shouldn't happen. */ |
| if (rec->lrh_index > next_idx) { |
| CERROR("missed desired record? %u > %u\n", |
| rec->lrh_index, next_idx); |
| return -ENOENT; |
| } |
| return 0; |
| } |
| return -EIO; |
| } |
| |
| static int llog_lvfs_prev_block(const struct lu_env *env, |
| struct llog_handle *loghandle, |
| int prev_idx, void *buf, int len) |
| { |
| __u64 cur_offset; |
| int rc; |
| |
| if (len == 0 || len & (LLOG_CHUNK_SIZE - 1)) |
| return -EINVAL; |
| |
| CDEBUG(D_OTHER, "looking for log index %u\n", prev_idx); |
| |
| cur_offset = LLOG_CHUNK_SIZE; |
| llog_skip_over(&cur_offset, 0, prev_idx); |
| |
| while (cur_offset < i_size_read(loghandle->lgh_file->f_dentry->d_inode)) { |
| struct llog_rec_hdr *rec, *last_rec; |
| struct llog_rec_tail *tail; |
| loff_t ppos = cur_offset; |
| |
| rc = fsfilt_read_record(loghandle->lgh_ctxt->loc_exp->exp_obd, |
| loghandle->lgh_file, buf, len, |
| &cur_offset); |
| if (rc < 0) { |
| CERROR("Cant read llog block at log id "DOSTID |
| "/%u offset "LPU64"\n", |
| POSTID(&loghandle->lgh_id.lgl_oi), |
| loghandle->lgh_id.lgl_ogen, |
| cur_offset); |
| return rc; |
| } |
| |
| /* put number of bytes read into rc to make code simpler */ |
| rc = cur_offset - ppos; |
| |
| if (rc == 0) /* end of file, nothing to do */ |
| return 0; |
| |
| if (rc < sizeof(*tail)) { |
| CERROR("Invalid llog block at log id "DOSTID"/%u offset" |
| LPU64"\n", POSTID(&loghandle->lgh_id.lgl_oi), |
| loghandle->lgh_id.lgl_ogen, cur_offset); |
| return -EINVAL; |
| } |
| |
| rec = buf; |
| if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) |
| lustre_swab_llog_rec(rec); |
| |
| tail = (struct llog_rec_tail *)(buf + rc - |
| sizeof(struct llog_rec_tail)); |
| |
| /* get the last record in block */ |
| last_rec = (struct llog_rec_hdr *)(buf + rc - |
| le32_to_cpu(tail->lrt_len)); |
| |
| if (LLOG_REC_HDR_NEEDS_SWABBING(last_rec)) |
| lustre_swab_llog_rec(last_rec); |
| LASSERT(last_rec->lrh_index == tail->lrt_index); |
| |
| /* this shouldn't happen */ |
| if (tail->lrt_index == 0) { |
| CERROR("Invalid llog tail at log id "DOSTID"/%u offset" |
| LPU64"\n", POSTID(&loghandle->lgh_id.lgl_oi), |
| loghandle->lgh_id.lgl_ogen, cur_offset); |
| return -EINVAL; |
| } |
| if (tail->lrt_index < prev_idx) |
| continue; |
| |
| /* sanity check that the start of the new buffer is no farther |
| * than the record that we wanted. This shouldn't happen. */ |
| if (rec->lrh_index > prev_idx) { |
| CERROR("missed desired record? %u > %u\n", |
| rec->lrh_index, prev_idx); |
| return -ENOENT; |
| } |
| return 0; |
| } |
| return -EIO; |
| } |
| |
| static struct file *llog_filp_open(char *dir, char *name, int flags, int mode) |
| { |
| char *logname; |
| struct file *filp; |
| int len; |
| |
| OBD_ALLOC(logname, PATH_MAX); |
| if (logname == NULL) |
| return ERR_PTR(-ENOMEM); |
| |
| len = snprintf(logname, PATH_MAX, "%s/%s", dir, name); |
| if (len >= PATH_MAX - 1) { |
| filp = ERR_PTR(-ENAMETOOLONG); |
| } else { |
| filp = l_filp_open(logname, flags, mode); |
| if (IS_ERR(filp) && PTR_ERR(filp) != -ENOENT) |
| CERROR("logfile creation %s: %ld\n", logname, |
| PTR_ERR(filp)); |
| } |
| OBD_FREE(logname, PATH_MAX); |
| return filp; |
| } |
| |
| static int llog_lvfs_open(const struct lu_env *env, struct llog_handle *handle, |
| struct llog_logid *logid, char *name, |
| enum llog_open_param open_param) |
| { |
| struct llog_ctxt *ctxt = handle->lgh_ctxt; |
| struct l_dentry *dchild = NULL; |
| struct obd_device *obd; |
| int rc = 0; |
| |
| LASSERT(ctxt); |
| LASSERT(ctxt->loc_exp); |
| LASSERT(ctxt->loc_exp->exp_obd); |
| obd = ctxt->loc_exp->exp_obd; |
| |
| LASSERT(handle); |
| if (logid != NULL) { |
| dchild = obd_lvfs_fid2dentry(ctxt->loc_exp, &logid->lgl_oi, |
| logid->lgl_ogen); |
| if (IS_ERR(dchild)) { |
| rc = PTR_ERR(dchild); |
| CERROR("%s: error looking up logfile #"DOSTID "#%08x:" |
| " rc = %d\n", ctxt->loc_obd->obd_name, |
| POSTID(&logid->lgl_oi), logid->lgl_ogen, rc); |
| GOTO(out, rc); |
| } |
| if (dchild->d_inode == NULL) { |
| l_dput(dchild); |
| rc = -ENOENT; |
| CERROR("%s: nonexistent llog #"DOSTID"#%08x:" |
| "rc = %d\n", ctxt->loc_obd->obd_name, |
| POSTID(&logid->lgl_oi), logid->lgl_ogen, rc); |
| GOTO(out, rc); |
| } |
| handle->lgh_file = l_dentry_open(&obd->obd_lvfs_ctxt, dchild, |
| O_RDWR | O_LARGEFILE); |
| l_dput(dchild); |
| if (IS_ERR(handle->lgh_file)) { |
| rc = PTR_ERR(handle->lgh_file); |
| handle->lgh_file = NULL; |
| CERROR("%s: error opening llog #"DOSTID"#%08x:" |
| "rc = %d\n", ctxt->loc_obd->obd_name, |
| POSTID(&logid->lgl_oi), logid->lgl_ogen, rc); |
| GOTO(out, rc); |
| } |
| handle->lgh_id = *logid; |
| } else if (name) { |
| handle->lgh_file = llog_filp_open(MOUNT_CONFIGS_DIR, name, |
| O_RDWR | O_LARGEFILE, 0644); |
| if (IS_ERR(handle->lgh_file)) { |
| rc = PTR_ERR(handle->lgh_file); |
| handle->lgh_file = NULL; |
| if (rc == -ENOENT && open_param == LLOG_OPEN_NEW) { |
| OBD_ALLOC(handle->lgh_name, strlen(name) + 1); |
| if (handle->lgh_name) |
| strcpy(handle->lgh_name, name); |
| else |
| GOTO(out, rc = -ENOMEM); |
| rc = 0; |
| } else { |
| GOTO(out, rc); |
| } |
| } else { |
| lustre_build_llog_lvfs_oid(&handle->lgh_id, |
| handle->lgh_file->f_dentry->d_inode->i_ino, |
| handle->lgh_file->f_dentry->d_inode->i_generation); |
| } |
| } else { |
| LASSERTF(open_param == LLOG_OPEN_NEW, "%#x\n", open_param); |
| handle->lgh_file = NULL; |
| } |
| |
| /* No new llog is expected but doesn't exist */ |
| if (open_param != LLOG_OPEN_NEW && handle->lgh_file == NULL) |
| GOTO(out_name, rc = -ENOENT); |
| |
| return 0; |
| out_name: |
| if (handle->lgh_name != NULL) |
| OBD_FREE(handle->lgh_name, strlen(name) + 1); |
| out: |
| return rc; |
| } |
| |
| static int llog_lvfs_exist(struct llog_handle *handle) |
| { |
| return (handle->lgh_file != NULL); |
| } |
| |
| /* This is a callback from the llog_* functions. |
| * Assumes caller has already pushed us into the kernel context. */ |
| static int llog_lvfs_create(const struct lu_env *env, |
| struct llog_handle *handle, |
| struct thandle *th) |
| { |
| struct llog_ctxt *ctxt = handle->lgh_ctxt; |
| struct obd_device *obd; |
| struct l_dentry *dchild = NULL; |
| struct file *file; |
| struct obdo *oa = NULL; |
| int rc = 0; |
| int open_flags = O_RDWR | O_CREAT | O_LARGEFILE; |
| |
| LASSERT(ctxt); |
| LASSERT(ctxt->loc_exp); |
| obd = ctxt->loc_exp->exp_obd; |
| LASSERT(handle->lgh_file == NULL); |
| |
| if (handle->lgh_name) { |
| file = llog_filp_open(MOUNT_CONFIGS_DIR, handle->lgh_name, |
| open_flags, 0644); |
| if (IS_ERR(file)) |
| return PTR_ERR(file); |
| |
| lustre_build_llog_lvfs_oid(&handle->lgh_id, |
| file->f_dentry->d_inode->i_ino, |
| file->f_dentry->d_inode->i_generation); |
| handle->lgh_file = file; |
| } else { |
| OBDO_ALLOC(oa); |
| if (oa == NULL) |
| return -ENOMEM; |
| |
| ostid_set_seq_llog(&oa->o_oi); |
| oa->o_valid = OBD_MD_FLGENER | OBD_MD_FLGROUP; |
| |
| rc = obd_create(NULL, ctxt->loc_exp, oa, NULL, NULL); |
| if (rc) |
| GOTO(out, rc); |
| |
| /* FIXME: rationalize the misuse of o_generation in |
| * this API along with mds_obd_{create,destroy}. |
| * Hopefully it is only an internal API issue. */ |
| #define o_generation o_parent_oid |
| dchild = obd_lvfs_fid2dentry(ctxt->loc_exp, &oa->o_oi, |
| oa->o_generation); |
| if (IS_ERR(dchild)) |
| GOTO(out, rc = PTR_ERR(dchild)); |
| |
| file = l_dentry_open(&obd->obd_lvfs_ctxt, dchild, open_flags); |
| l_dput(dchild); |
| if (IS_ERR(file)) |
| GOTO(out, rc = PTR_ERR(file)); |
| handle->lgh_id.lgl_oi = oa->o_oi; |
| handle->lgh_id.lgl_ogen = oa->o_generation; |
| handle->lgh_file = file; |
| out: |
| OBDO_FREE(oa); |
| } |
| return rc; |
| } |
| |
| static int llog_lvfs_close(const struct lu_env *env, |
| struct llog_handle *handle) |
| { |
| int rc; |
| |
| if (handle->lgh_file == NULL) |
| return 0; |
| rc = filp_close(handle->lgh_file, 0); |
| if (rc) |
| CERROR("%s: error closing llog #"DOSTID"#%08x: " |
| "rc = %d\n", handle->lgh_ctxt->loc_obd->obd_name, |
| POSTID(&handle->lgh_id.lgl_oi), |
| handle->lgh_id.lgl_ogen, rc); |
| handle->lgh_file = NULL; |
| if (handle->lgh_name) { |
| OBD_FREE(handle->lgh_name, strlen(handle->lgh_name) + 1); |
| handle->lgh_name = NULL; |
| } |
| return rc; |
| } |
| |
| static int llog_lvfs_destroy(const struct lu_env *env, |
| struct llog_handle *handle) |
| { |
| struct dentry *fdentry; |
| struct obdo *oa; |
| struct obd_device *obd = handle->lgh_ctxt->loc_exp->exp_obd; |
| char *dir; |
| void *th; |
| struct inode *inode; |
| int rc, rc1; |
| |
| dir = MOUNT_CONFIGS_DIR; |
| |
| LASSERT(handle->lgh_file); |
| fdentry = handle->lgh_file->f_dentry; |
| inode = fdentry->d_parent->d_inode; |
| if (strcmp(fdentry->d_parent->d_name.name, dir) == 0) { |
| struct lvfs_run_ctxt saved; |
| struct vfsmount *mnt = mntget(handle->lgh_file->f_vfsmnt); |
| |
| push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); |
| dget(fdentry); |
| rc = llog_lvfs_close(env, handle); |
| if (rc == 0) { |
| mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); |
| rc = ll_vfs_unlink(inode, fdentry, mnt); |
| mutex_unlock(&inode->i_mutex); |
| } |
| mntput(mnt); |
| |
| dput(fdentry); |
| pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); |
| return rc; |
| } |
| |
| OBDO_ALLOC(oa); |
| if (oa == NULL) |
| return -ENOMEM; |
| |
| oa->o_oi = handle->lgh_id.lgl_oi; |
| oa->o_generation = handle->lgh_id.lgl_ogen; |
| #undef o_generation |
| oa->o_valid = OBD_MD_FLID | OBD_MD_FLGROUP | OBD_MD_FLGENER; |
| |
| rc = llog_lvfs_close(env, handle); |
| if (rc) |
| GOTO(out, rc); |
| |
| th = fsfilt_start_log(obd, inode, FSFILT_OP_UNLINK, NULL, 1); |
| if (IS_ERR(th)) { |
| CERROR("fsfilt_start failed: %ld\n", PTR_ERR(th)); |
| GOTO(out, rc = PTR_ERR(th)); |
| } |
| |
| rc = obd_destroy(NULL, handle->lgh_ctxt->loc_exp, oa, |
| NULL, NULL, NULL, NULL); |
| |
| rc1 = fsfilt_commit(obd, inode, th, 0); |
| if (rc == 0 && rc1 != 0) |
| rc = rc1; |
| out: |
| OBDO_FREE(oa); |
| return rc; |
| } |
| |
| static int llog_lvfs_declare_create(const struct lu_env *env, |
| struct llog_handle *res, |
| struct thandle *th) |
| { |
| return 0; |
| } |
| |
| static int llog_lvfs_declare_write_rec(const struct lu_env *env, |
| struct llog_handle *loghandle, |
| struct llog_rec_hdr *rec, |
| int idx, struct thandle *th) |
| { |
| return 0; |
| } |
| |
| struct llog_operations llog_lvfs_ops = { |
| .lop_write_rec = llog_lvfs_write_rec, |
| .lop_next_block = llog_lvfs_next_block, |
| .lop_prev_block = llog_lvfs_prev_block, |
| .lop_read_header = llog_lvfs_read_header, |
| .lop_create = llog_lvfs_create, |
| .lop_destroy = llog_lvfs_destroy, |
| .lop_close = llog_lvfs_close, |
| .lop_open = llog_lvfs_open, |
| .lop_exist = llog_lvfs_exist, |
| .lop_declare_create = llog_lvfs_declare_create, |
| .lop_declare_write_rec = llog_lvfs_declare_write_rec, |
| }; |
| EXPORT_SYMBOL(llog_lvfs_ops); |
| #else /* !__KERNEL__ */ |
| struct llog_operations llog_lvfs_ops = {}; |
| #endif |