| /* |
| * 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) 2007, 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/dt_object.c |
| * |
| * Dt Object. |
| * Generic functions from dt_object.h |
| * |
| * Author: Nikita Danilov <nikita@clusterfs.com> |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_CLASS |
| |
| #include "../include/obd.h" |
| #include "../include/dt_object.h" |
| #include <linux/list.h> |
| /* fid_be_to_cpu() */ |
| #include "../include/lustre_fid.h" |
| |
| #include "../include/lustre_quota.h" |
| |
| /* context key constructor/destructor: dt_global_key_init, dt_global_key_fini */ |
| LU_KEY_INIT(dt_global, struct dt_thread_info); |
| LU_KEY_FINI(dt_global, struct dt_thread_info); |
| |
| struct lu_context_key dt_key = { |
| .lct_tags = LCT_MD_THREAD | LCT_DT_THREAD | LCT_MG_THREAD | LCT_LOCAL, |
| .lct_init = dt_global_key_init, |
| .lct_fini = dt_global_key_fini |
| }; |
| EXPORT_SYMBOL(dt_key); |
| |
| /* no lock is necessary to protect the list, because call-backs |
| * are added during system startup. Please refer to "struct dt_device". |
| */ |
| void dt_txn_callback_add(struct dt_device *dev, struct dt_txn_callback *cb) |
| { |
| list_add(&cb->dtc_linkage, &dev->dd_txn_callbacks); |
| } |
| EXPORT_SYMBOL(dt_txn_callback_add); |
| |
| void dt_txn_callback_del(struct dt_device *dev, struct dt_txn_callback *cb) |
| { |
| list_del_init(&cb->dtc_linkage); |
| } |
| EXPORT_SYMBOL(dt_txn_callback_del); |
| |
| int dt_txn_hook_start(const struct lu_env *env, |
| struct dt_device *dev, struct thandle *th) |
| { |
| int rc = 0; |
| struct dt_txn_callback *cb; |
| |
| if (th->th_local) |
| return 0; |
| |
| list_for_each_entry(cb, &dev->dd_txn_callbacks, dtc_linkage) { |
| if (cb->dtc_txn_start == NULL || |
| !(cb->dtc_tag & env->le_ctx.lc_tags)) |
| continue; |
| rc = cb->dtc_txn_start(env, th, cb->dtc_cookie); |
| if (rc < 0) |
| break; |
| } |
| return rc; |
| } |
| EXPORT_SYMBOL(dt_txn_hook_start); |
| |
| int dt_txn_hook_stop(const struct lu_env *env, struct thandle *txn) |
| { |
| struct dt_device *dev = txn->th_dev; |
| struct dt_txn_callback *cb; |
| int rc = 0; |
| |
| if (txn->th_local) |
| return 0; |
| |
| list_for_each_entry(cb, &dev->dd_txn_callbacks, dtc_linkage) { |
| if (cb->dtc_txn_stop == NULL || |
| !(cb->dtc_tag & env->le_ctx.lc_tags)) |
| continue; |
| rc = cb->dtc_txn_stop(env, txn, cb->dtc_cookie); |
| if (rc < 0) |
| break; |
| } |
| return rc; |
| } |
| EXPORT_SYMBOL(dt_txn_hook_stop); |
| |
| void dt_txn_hook_commit(struct thandle *txn) |
| { |
| struct dt_txn_callback *cb; |
| |
| if (txn->th_local) |
| return; |
| |
| list_for_each_entry(cb, &txn->th_dev->dd_txn_callbacks, |
| dtc_linkage) { |
| if (cb->dtc_txn_commit) |
| cb->dtc_txn_commit(txn, cb->dtc_cookie); |
| } |
| } |
| EXPORT_SYMBOL(dt_txn_hook_commit); |
| |
| int dt_device_init(struct dt_device *dev, struct lu_device_type *t) |
| { |
| |
| INIT_LIST_HEAD(&dev->dd_txn_callbacks); |
| return lu_device_init(&dev->dd_lu_dev, t); |
| } |
| EXPORT_SYMBOL(dt_device_init); |
| |
| void dt_device_fini(struct dt_device *dev) |
| { |
| lu_device_fini(&dev->dd_lu_dev); |
| } |
| EXPORT_SYMBOL(dt_device_fini); |
| |
| int dt_object_init(struct dt_object *obj, |
| struct lu_object_header *h, struct lu_device *d) |
| |
| { |
| return lu_object_init(&obj->do_lu, h, d); |
| } |
| EXPORT_SYMBOL(dt_object_init); |
| |
| void dt_object_fini(struct dt_object *obj) |
| { |
| lu_object_fini(&obj->do_lu); |
| } |
| EXPORT_SYMBOL(dt_object_fini); |
| |
| int dt_try_as_dir(const struct lu_env *env, struct dt_object *obj) |
| { |
| if (obj->do_index_ops == NULL) |
| obj->do_ops->do_index_try(env, obj, &dt_directory_features); |
| return obj->do_index_ops != NULL; |
| } |
| EXPORT_SYMBOL(dt_try_as_dir); |
| |
| enum dt_format_type dt_mode_to_dft(__u32 mode) |
| { |
| enum dt_format_type result; |
| |
| switch (mode & S_IFMT) { |
| case S_IFDIR: |
| result = DFT_DIR; |
| break; |
| case S_IFREG: |
| result = DFT_REGULAR; |
| break; |
| case S_IFLNK: |
| result = DFT_SYM; |
| break; |
| case S_IFCHR: |
| case S_IFBLK: |
| case S_IFIFO: |
| case S_IFSOCK: |
| result = DFT_NODE; |
| break; |
| default: |
| LBUG(); |
| break; |
| } |
| return result; |
| } |
| EXPORT_SYMBOL(dt_mode_to_dft); |
| |
| /** |
| * lookup fid for object named \a name in directory \a dir. |
| */ |
| |
| int dt_lookup_dir(const struct lu_env *env, struct dt_object *dir, |
| const char *name, struct lu_fid *fid) |
| { |
| if (dt_try_as_dir(env, dir)) |
| return dt_lookup(env, dir, (struct dt_rec *)fid, |
| (const struct dt_key *)name, BYPASS_CAPA); |
| return -ENOTDIR; |
| } |
| EXPORT_SYMBOL(dt_lookup_dir); |
| |
| /* this differs from dt_locate by top_dev as parameter |
| * but not one from lu_site */ |
| struct dt_object *dt_locate_at(const struct lu_env *env, |
| struct dt_device *dev, const struct lu_fid *fid, |
| struct lu_device *top_dev) |
| { |
| struct lu_object *lo, *n; |
| |
| lo = lu_object_find_at(env, top_dev, fid, NULL); |
| if (IS_ERR(lo)) |
| return (void *)lo; |
| |
| LASSERT(lo != NULL); |
| |
| list_for_each_entry(n, &lo->lo_header->loh_layers, lo_linkage) { |
| if (n->lo_dev == &dev->dd_lu_dev) |
| return container_of0(n, struct dt_object, do_lu); |
| } |
| return ERR_PTR(-ENOENT); |
| } |
| EXPORT_SYMBOL(dt_locate_at); |
| |
| /** |
| * find a object named \a entry in given \a dfh->dfh_o directory. |
| */ |
| static int dt_find_entry(const struct lu_env *env, const char *entry, void *data) |
| { |
| struct dt_find_hint *dfh = data; |
| struct dt_device *dt = dfh->dfh_dt; |
| struct lu_fid *fid = dfh->dfh_fid; |
| struct dt_object *obj = dfh->dfh_o; |
| int result; |
| |
| result = dt_lookup_dir(env, obj, entry, fid); |
| lu_object_put(env, &obj->do_lu); |
| if (result == 0) { |
| obj = dt_locate(env, dt, fid); |
| if (IS_ERR(obj)) |
| result = PTR_ERR(obj); |
| } |
| dfh->dfh_o = obj; |
| return result; |
| } |
| |
| /** |
| * Abstract function which parses path name. This function feeds |
| * path component to \a entry_func. |
| */ |
| int dt_path_parser(const struct lu_env *env, |
| char *path, dt_entry_func_t entry_func, |
| void *data) |
| { |
| char *e; |
| int rc = 0; |
| |
| while (1) { |
| e = strsep(&path, "/"); |
| if (e == NULL) |
| break; |
| |
| if (e[0] == 0) { |
| if (!path || path[0] == '\0') |
| break; |
| continue; |
| } |
| rc = entry_func(env, e, data); |
| if (rc) |
| break; |
| } |
| |
| return rc; |
| } |
| |
| struct dt_object * |
| dt_store_resolve(const struct lu_env *env, struct dt_device *dt, |
| const char *path, struct lu_fid *fid) |
| { |
| struct dt_thread_info *info = dt_info(env); |
| struct dt_find_hint *dfh = &info->dti_dfh; |
| struct dt_object *obj; |
| char *local = info->dti_buf; |
| int result; |
| |
| |
| dfh->dfh_dt = dt; |
| dfh->dfh_fid = fid; |
| |
| strncpy(local, path, DT_MAX_PATH); |
| local[DT_MAX_PATH - 1] = '\0'; |
| |
| result = dt->dd_ops->dt_root_get(env, dt, fid); |
| if (result == 0) { |
| obj = dt_locate(env, dt, fid); |
| if (!IS_ERR(obj)) { |
| dfh->dfh_o = obj; |
| result = dt_path_parser(env, local, dt_find_entry, dfh); |
| if (result != 0) |
| obj = ERR_PTR(result); |
| else |
| obj = dfh->dfh_o; |
| } |
| } else { |
| obj = ERR_PTR(result); |
| } |
| return obj; |
| } |
| EXPORT_SYMBOL(dt_store_resolve); |
| |
| static struct dt_object *dt_reg_open(const struct lu_env *env, |
| struct dt_device *dt, |
| struct dt_object *p, |
| const char *name, |
| struct lu_fid *fid) |
| { |
| struct dt_object *o; |
| int result; |
| |
| result = dt_lookup_dir(env, p, name, fid); |
| if (result == 0){ |
| o = dt_locate(env, dt, fid); |
| } else |
| o = ERR_PTR(result); |
| |
| return o; |
| } |
| |
| /** |
| * Open dt object named \a filename from \a dirname directory. |
| * \param dt dt device |
| * \param fid on success, object fid is stored in *fid |
| */ |
| struct dt_object *dt_store_open(const struct lu_env *env, |
| struct dt_device *dt, |
| const char *dirname, |
| const char *filename, |
| struct lu_fid *fid) |
| { |
| struct dt_object *file; |
| struct dt_object *dir; |
| |
| dir = dt_store_resolve(env, dt, dirname, fid); |
| if (!IS_ERR(dir)) { |
| file = dt_reg_open(env, dt, dir, |
| filename, fid); |
| lu_object_put(env, &dir->do_lu); |
| } else { |
| file = dir; |
| } |
| return file; |
| } |
| EXPORT_SYMBOL(dt_store_open); |
| |
| struct dt_object *dt_find_or_create(const struct lu_env *env, |
| struct dt_device *dt, |
| const struct lu_fid *fid, |
| struct dt_object_format *dof, |
| struct lu_attr *at) |
| { |
| struct dt_object *dto; |
| struct thandle *th; |
| int rc; |
| |
| dto = dt_locate(env, dt, fid); |
| if (IS_ERR(dto)) |
| return dto; |
| |
| LASSERT(dto != NULL); |
| if (dt_object_exists(dto)) |
| return dto; |
| |
| th = dt_trans_create(env, dt); |
| if (IS_ERR(th)) { |
| rc = PTR_ERR(th); |
| goto out; |
| } |
| |
| rc = dt_declare_create(env, dto, at, NULL, dof, th); |
| if (rc) |
| goto trans_stop; |
| |
| rc = dt_trans_start_local(env, dt, th); |
| if (rc) |
| goto trans_stop; |
| |
| dt_write_lock(env, dto, 0); |
| if (dt_object_exists(dto)) { |
| rc = 0; |
| goto unlock; |
| } |
| |
| CDEBUG(D_OTHER, "create new object "DFID"\n", PFID(fid)); |
| |
| rc = dt_create(env, dto, at, NULL, dof, th); |
| if (rc) |
| goto unlock; |
| LASSERT(dt_object_exists(dto)); |
| unlock: |
| dt_write_unlock(env, dto); |
| trans_stop: |
| dt_trans_stop(env, dt, th); |
| out: |
| if (rc) { |
| lu_object_put(env, &dto->do_lu); |
| return ERR_PTR(rc); |
| } |
| return dto; |
| } |
| EXPORT_SYMBOL(dt_find_or_create); |
| |
| /* dt class init function. */ |
| int dt_global_init(void) |
| { |
| LU_CONTEXT_KEY_INIT(&dt_key); |
| return lu_context_key_register(&dt_key); |
| } |
| |
| void dt_global_fini(void) |
| { |
| lu_context_key_degister(&dt_key); |
| } |
| |
| /** |
| * Generic read helper. May return an error for partial reads. |
| * |
| * \param env lustre environment |
| * \param dt object to be read |
| * \param buf lu_buf to be filled, with buffer pointer and length |
| * \param pos position to start reading, updated as data is read |
| * |
| * \retval real size of data read |
| * \retval -ve errno on failure |
| */ |
| int dt_read(const struct lu_env *env, struct dt_object *dt, |
| struct lu_buf *buf, loff_t *pos) |
| { |
| LASSERTF(dt != NULL, "dt is NULL when we want to read record\n"); |
| return dt->do_body_ops->dbo_read(env, dt, buf, pos, BYPASS_CAPA); |
| } |
| EXPORT_SYMBOL(dt_read); |
| |
| /** |
| * Read structures of fixed size from storage. Unlike dt_read(), using |
| * dt_record_read() will return an error for partial reads. |
| * |
| * \param env lustre environment |
| * \param dt object to be read |
| * \param buf lu_buf to be filled, with buffer pointer and length |
| * \param pos position to start reading, updated as data is read |
| * |
| * \retval 0 on successfully reading full buffer |
| * \retval -EFAULT on short read |
| * \retval -ve errno on failure |
| */ |
| int dt_record_read(const struct lu_env *env, struct dt_object *dt, |
| struct lu_buf *buf, loff_t *pos) |
| { |
| int rc; |
| |
| LASSERTF(dt != NULL, "dt is NULL when we want to read record\n"); |
| |
| rc = dt->do_body_ops->dbo_read(env, dt, buf, pos, BYPASS_CAPA); |
| |
| if (rc == buf->lb_len) |
| rc = 0; |
| else if (rc >= 0) |
| rc = -EFAULT; |
| return rc; |
| } |
| EXPORT_SYMBOL(dt_record_read); |
| |
| int dt_record_write(const struct lu_env *env, struct dt_object *dt, |
| const struct lu_buf *buf, loff_t *pos, struct thandle *th) |
| { |
| int rc; |
| |
| LASSERTF(dt != NULL, "dt is NULL when we want to write record\n"); |
| LASSERT(th != NULL); |
| LASSERT(dt->do_body_ops); |
| LASSERT(dt->do_body_ops->dbo_write); |
| rc = dt->do_body_ops->dbo_write(env, dt, buf, pos, th, BYPASS_CAPA, 1); |
| if (rc == buf->lb_len) |
| rc = 0; |
| else if (rc >= 0) |
| rc = -EFAULT; |
| return rc; |
| } |
| EXPORT_SYMBOL(dt_record_write); |
| |
| int dt_declare_version_set(const struct lu_env *env, struct dt_object *o, |
| struct thandle *th) |
| { |
| struct lu_buf vbuf; |
| char *xname = XATTR_NAME_VERSION; |
| |
| LASSERT(o); |
| vbuf.lb_buf = NULL; |
| vbuf.lb_len = sizeof(dt_obj_version_t); |
| return dt_declare_xattr_set(env, o, &vbuf, xname, 0, th); |
| |
| } |
| EXPORT_SYMBOL(dt_declare_version_set); |
| |
| void dt_version_set(const struct lu_env *env, struct dt_object *o, |
| dt_obj_version_t version, struct thandle *th) |
| { |
| struct lu_buf vbuf; |
| char *xname = XATTR_NAME_VERSION; |
| int rc; |
| |
| LASSERT(o); |
| vbuf.lb_buf = &version; |
| vbuf.lb_len = sizeof(version); |
| |
| rc = dt_xattr_set(env, o, &vbuf, xname, 0, th, BYPASS_CAPA); |
| if (rc < 0) |
| CDEBUG(D_INODE, "Can't set version, rc %d\n", rc); |
| return; |
| } |
| EXPORT_SYMBOL(dt_version_set); |
| |
| dt_obj_version_t dt_version_get(const struct lu_env *env, struct dt_object *o) |
| { |
| struct lu_buf vbuf; |
| char *xname = XATTR_NAME_VERSION; |
| dt_obj_version_t version; |
| int rc; |
| |
| LASSERT(o); |
| vbuf.lb_buf = &version; |
| vbuf.lb_len = sizeof(version); |
| rc = dt_xattr_get(env, o, &vbuf, xname, BYPASS_CAPA); |
| if (rc != sizeof(version)) { |
| CDEBUG(D_INODE, "Can't get version, rc %d\n", rc); |
| version = 0; |
| } |
| return version; |
| } |
| EXPORT_SYMBOL(dt_version_get); |
| |
| /* list of all supported index types */ |
| |
| /* directories */ |
| const struct dt_index_features dt_directory_features; |
| EXPORT_SYMBOL(dt_directory_features); |
| |
| /* scrub iterator */ |
| const struct dt_index_features dt_otable_features; |
| EXPORT_SYMBOL(dt_otable_features); |
| |
| /* lfsck */ |
| const struct dt_index_features dt_lfsck_features = { |
| .dif_flags = DT_IND_UPDATE, |
| .dif_keysize_min = sizeof(struct lu_fid), |
| .dif_keysize_max = sizeof(struct lu_fid), |
| .dif_recsize_min = sizeof(__u8), |
| .dif_recsize_max = sizeof(__u8), |
| .dif_ptrsize = 4 |
| }; |
| EXPORT_SYMBOL(dt_lfsck_features); |
| |
| /* accounting indexes */ |
| const struct dt_index_features dt_acct_features = { |
| .dif_flags = DT_IND_UPDATE, |
| .dif_keysize_min = sizeof(__u64), /* 64-bit uid/gid */ |
| .dif_keysize_max = sizeof(__u64), /* 64-bit uid/gid */ |
| .dif_recsize_min = sizeof(struct lquota_acct_rec), /* 16 bytes */ |
| .dif_recsize_max = sizeof(struct lquota_acct_rec), /* 16 bytes */ |
| .dif_ptrsize = 4 |
| }; |
| EXPORT_SYMBOL(dt_acct_features); |
| |
| /* global quota files */ |
| const struct dt_index_features dt_quota_glb_features = { |
| .dif_flags = DT_IND_UPDATE, |
| /* a different key would have to be used for per-directory quota */ |
| .dif_keysize_min = sizeof(__u64), /* 64-bit uid/gid */ |
| .dif_keysize_max = sizeof(__u64), /* 64-bit uid/gid */ |
| .dif_recsize_min = sizeof(struct lquota_glb_rec), /* 32 bytes */ |
| .dif_recsize_max = sizeof(struct lquota_glb_rec), /* 32 bytes */ |
| .dif_ptrsize = 4 |
| }; |
| EXPORT_SYMBOL(dt_quota_glb_features); |
| |
| /* slave quota files */ |
| const struct dt_index_features dt_quota_slv_features = { |
| .dif_flags = DT_IND_UPDATE, |
| /* a different key would have to be used for per-directory quota */ |
| .dif_keysize_min = sizeof(__u64), /* 64-bit uid/gid */ |
| .dif_keysize_max = sizeof(__u64), /* 64-bit uid/gid */ |
| .dif_recsize_min = sizeof(struct lquota_slv_rec), /* 8 bytes */ |
| .dif_recsize_max = sizeof(struct lquota_slv_rec), /* 8 bytes */ |
| .dif_ptrsize = 4 |
| }; |
| EXPORT_SYMBOL(dt_quota_slv_features); |
| |
| /* helper function returning what dt_index_features structure should be used |
| * based on the FID sequence. This is used by OBD_IDX_READ RPC */ |
| static inline const struct dt_index_features *dt_index_feat_select(__u64 seq, |
| __u32 mode) |
| { |
| if (seq == FID_SEQ_QUOTA_GLB) { |
| /* global quota index */ |
| if (!S_ISREG(mode)) |
| /* global quota index should be a regular file */ |
| return ERR_PTR(-ENOENT); |
| return &dt_quota_glb_features; |
| } else if (seq == FID_SEQ_QUOTA) { |
| /* quota slave index */ |
| if (!S_ISREG(mode)) |
| /* slave index should be a regular file */ |
| return ERR_PTR(-ENOENT); |
| return &dt_quota_slv_features; |
| } else if (seq >= FID_SEQ_NORMAL) { |
| /* object is part of the namespace, verify that it is a |
| * directory */ |
| if (!S_ISDIR(mode)) |
| /* sorry, we can only deal with directory */ |
| return ERR_PTR(-ENOTDIR); |
| return &dt_directory_features; |
| } |
| |
| return ERR_PTR(-EOPNOTSUPP); |
| } |
| |
| /* |
| * Fill a lu_idxpage with key/record pairs read for transfer via OBD_IDX_READ |
| * RPC |
| * |
| * \param env - is the environment passed by the caller |
| * \param lp - is a pointer to the lu_page to fill |
| * \param nob - is the maximum number of bytes that should be copied |
| * \param iops - is the index operation vector associated with the index object |
| * \param it - is a pointer to the current iterator |
| * \param attr - is the index attribute to pass to iops->rec() |
| * \param arg - is a pointer to the idx_info structure |
| */ |
| static int dt_index_page_build(const struct lu_env *env, union lu_page *lp, |
| int nob, const struct dt_it_ops *iops, |
| struct dt_it *it, __u32 attr, void *arg) |
| { |
| struct idx_info *ii = (struct idx_info *)arg; |
| struct lu_idxpage *lip = &lp->lp_idx; |
| char *entry; |
| int rc, size; |
| |
| /* no support for variable key & record size for now */ |
| LASSERT((ii->ii_flags & II_FL_VARKEY) == 0); |
| LASSERT((ii->ii_flags & II_FL_VARREC) == 0); |
| |
| /* initialize the header of the new container */ |
| memset(lip, 0, LIP_HDR_SIZE); |
| lip->lip_magic = LIP_MAGIC; |
| nob -= LIP_HDR_SIZE; |
| |
| /* compute size needed to store a key/record pair */ |
| size = ii->ii_recsize + ii->ii_keysize; |
| if ((ii->ii_flags & II_FL_NOHASH) == 0) |
| /* add hash if the client wants it */ |
| size += sizeof(__u64); |
| |
| entry = lip->lip_entries; |
| do { |
| char *tmp_entry = entry; |
| struct dt_key *key; |
| __u64 hash; |
| |
| /* fetch 64-bit hash value */ |
| hash = iops->store(env, it); |
| ii->ii_hash_end = hash; |
| |
| if (OBD_FAIL_CHECK(OBD_FAIL_OBD_IDX_READ_BREAK)) { |
| if (lip->lip_nr != 0) { |
| rc = 0; |
| goto out; |
| } |
| } |
| |
| if (nob < size) { |
| if (lip->lip_nr == 0) |
| rc = -EINVAL; |
| else |
| rc = 0; |
| goto out; |
| } |
| |
| if ((ii->ii_flags & II_FL_NOHASH) == 0) { |
| /* client wants to the 64-bit hash value associated with |
| * each record */ |
| memcpy(tmp_entry, &hash, sizeof(hash)); |
| tmp_entry += sizeof(hash); |
| } |
| |
| /* then the key value */ |
| LASSERT(iops->key_size(env, it) == ii->ii_keysize); |
| key = iops->key(env, it); |
| memcpy(tmp_entry, key, ii->ii_keysize); |
| tmp_entry += ii->ii_keysize; |
| |
| /* and finally the record */ |
| rc = iops->rec(env, it, (struct dt_rec *)tmp_entry, attr); |
| if (rc != -ESTALE) { |
| if (rc != 0) |
| goto out; |
| |
| /* hash/key/record successfully copied! */ |
| lip->lip_nr++; |
| if (unlikely(lip->lip_nr == 1 && ii->ii_count == 0)) |
| ii->ii_hash_start = hash; |
| entry = tmp_entry + ii->ii_recsize; |
| nob -= size; |
| } |
| |
| /* move on to the next record */ |
| do { |
| rc = iops->next(env, it); |
| } while (rc == -ESTALE); |
| |
| } while (rc == 0); |
| |
| goto out; |
| out: |
| if (rc >= 0 && lip->lip_nr > 0) |
| /* one more container */ |
| ii->ii_count++; |
| if (rc > 0) |
| /* no more entries */ |
| ii->ii_hash_end = II_END_OFF; |
| return rc; |
| } |
| |
| /* |
| * Walk index and fill lu_page containers with key/record pairs |
| * |
| * \param env - is the environment passed by the caller |
| * \param obj - is the index object to parse |
| * \param rdpg - is the lu_rdpg descriptor associated with the transfer |
| * \param filler - is the callback function responsible for filling a lu_page |
| * with key/record pairs in the format wanted by the caller |
| * \param arg - is an opaq argument passed to the filler function |
| * |
| * \retval sum (in bytes) of all filled lu_pages |
| * \retval -ve errno on failure |
| */ |
| int dt_index_walk(const struct lu_env *env, struct dt_object *obj, |
| const struct lu_rdpg *rdpg, dt_index_page_build_t filler, |
| void *arg) |
| { |
| struct dt_it *it; |
| const struct dt_it_ops *iops; |
| unsigned int pageidx, nob, nlupgs = 0; |
| int rc; |
| |
| LASSERT(rdpg->rp_pages != NULL); |
| LASSERT(obj->do_index_ops != NULL); |
| |
| nob = rdpg->rp_count; |
| if (nob <= 0) |
| return -EFAULT; |
| |
| /* Iterate through index and fill containers from @rdpg */ |
| iops = &obj->do_index_ops->dio_it; |
| LASSERT(iops != NULL); |
| it = iops->init(env, obj, rdpg->rp_attrs, BYPASS_CAPA); |
| if (IS_ERR(it)) |
| return PTR_ERR(it); |
| |
| rc = iops->load(env, it, rdpg->rp_hash); |
| if (rc == 0) { |
| /* |
| * Iterator didn't find record with exactly the key requested. |
| * |
| * It is currently either |
| * |
| * - positioned above record with key less than |
| * requested---skip it. |
| * - or not positioned at all (is in IAM_IT_SKEWED |
| * state)---position it on the next item. |
| */ |
| rc = iops->next(env, it); |
| } else if (rc > 0) { |
| rc = 0; |
| } |
| |
| /* Fill containers one after the other. There might be multiple |
| * containers per physical page. |
| * |
| * At this point and across for-loop: |
| * rc == 0 -> ok, proceed. |
| * rc > 0 -> end of index. |
| * rc < 0 -> error. */ |
| for (pageidx = 0; rc == 0 && nob > 0; pageidx++) { |
| union lu_page *lp; |
| int i; |
| |
| LASSERT(pageidx < rdpg->rp_npages); |
| lp = kmap(rdpg->rp_pages[pageidx]); |
| |
| /* fill lu pages */ |
| for (i = 0; i < LU_PAGE_COUNT; i++, lp++, nob -= LU_PAGE_SIZE) { |
| rc = filler(env, lp, min_t(int, nob, LU_PAGE_SIZE), |
| iops, it, rdpg->rp_attrs, arg); |
| if (rc < 0) |
| break; |
| /* one more lu_page */ |
| nlupgs++; |
| if (rc > 0) |
| /* end of index */ |
| break; |
| } |
| kunmap(rdpg->rp_pages[i]); |
| } |
| |
| iops->put(env, it); |
| iops->fini(env, it); |
| |
| if (rc >= 0) |
| rc = min_t(unsigned int, nlupgs * LU_PAGE_SIZE, rdpg->rp_count); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(dt_index_walk); |
| |
| /** |
| * Walk key/record pairs of an index and copy them into 4KB containers to be |
| * transferred over the network. This is the common handler for OBD_IDX_READ |
| * RPC processing. |
| * |
| * \param env - is the environment passed by the caller |
| * \param dev - is the dt_device storing the index |
| * \param ii - is the idx_info structure packed by the client in the |
| * OBD_IDX_READ request |
| * \param rdpg - is the lu_rdpg descriptor |
| * |
| * \retval on success, return sum (in bytes) of all filled containers |
| * \retval appropriate error otherwise. |
| */ |
| int dt_index_read(const struct lu_env *env, struct dt_device *dev, |
| struct idx_info *ii, const struct lu_rdpg *rdpg) |
| { |
| const struct dt_index_features *feat; |
| struct dt_object *obj; |
| int rc; |
| |
| /* rp_count shouldn't be null and should be a multiple of the container |
| * size */ |
| if (rdpg->rp_count <= 0 && (rdpg->rp_count & (LU_PAGE_SIZE - 1)) != 0) |
| return -EFAULT; |
| |
| if (fid_seq(&ii->ii_fid) >= FID_SEQ_NORMAL) |
| /* we don't support directory transfer via OBD_IDX_READ for the |
| * time being */ |
| return -EOPNOTSUPP; |
| |
| if (!fid_is_quota(&ii->ii_fid)) |
| /* block access to all local files except quota files */ |
| return -EPERM; |
| |
| /* lookup index object subject to the transfer */ |
| obj = dt_locate(env, dev, &ii->ii_fid); |
| if (IS_ERR(obj)) |
| return PTR_ERR(obj); |
| if (dt_object_exists(obj) == 0) { |
| rc = -ENOENT; |
| goto out; |
| } |
| |
| /* fetch index features associated with index object */ |
| feat = dt_index_feat_select(fid_seq(&ii->ii_fid), |
| lu_object_attr(&obj->do_lu)); |
| if (IS_ERR(feat)) { |
| rc = PTR_ERR(feat); |
| goto out; |
| } |
| |
| /* load index feature if not done already */ |
| if (obj->do_index_ops == NULL) { |
| rc = obj->do_ops->do_index_try(env, obj, feat); |
| if (rc) |
| goto out; |
| } |
| |
| /* fill ii_flags with supported index features */ |
| ii->ii_flags &= II_FL_NOHASH; |
| |
| ii->ii_keysize = feat->dif_keysize_max; |
| if ((feat->dif_flags & DT_IND_VARKEY) != 0) { |
| /* key size is variable */ |
| ii->ii_flags |= II_FL_VARKEY; |
| /* we don't support variable key size for the time being */ |
| rc = -EOPNOTSUPP; |
| goto out; |
| } |
| |
| ii->ii_recsize = feat->dif_recsize_max; |
| if ((feat->dif_flags & DT_IND_VARREC) != 0) { |
| /* record size is variable */ |
| ii->ii_flags |= II_FL_VARREC; |
| /* we don't support variable record size for the time being */ |
| rc = -EOPNOTSUPP; |
| goto out; |
| } |
| |
| if ((feat->dif_flags & DT_IND_NONUNQ) != 0) |
| /* key isn't necessarily unique */ |
| ii->ii_flags |= II_FL_NONUNQ; |
| |
| dt_read_lock(env, obj, 0); |
| /* fetch object version before walking the index */ |
| ii->ii_version = dt_version_get(env, obj); |
| |
| /* walk the index and fill lu_idxpages with key/record pairs */ |
| rc = dt_index_walk(env, obj, rdpg, dt_index_page_build ,ii); |
| dt_read_unlock(env, obj); |
| |
| if (rc == 0) { |
| /* index is empty */ |
| LASSERT(ii->ii_count == 0); |
| ii->ii_hash_end = II_END_OFF; |
| } |
| |
| goto out; |
| out: |
| lu_object_put(env, &obj->do_lu); |
| return rc; |
| } |
| EXPORT_SYMBOL(dt_index_read); |
| |
| #if defined (CONFIG_PROC_FS) |
| |
| int lprocfs_dt_rd_blksize(char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct dt_device *dt = data; |
| struct obd_statfs osfs; |
| int rc = dt_statfs(NULL, dt, &osfs); |
| |
| if (rc == 0) { |
| *eof = 1; |
| rc = snprintf(page, count, "%u\n", |
| (unsigned) osfs.os_bsize); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(lprocfs_dt_rd_blksize); |
| |
| int lprocfs_dt_rd_kbytestotal(char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct dt_device *dt = data; |
| struct obd_statfs osfs; |
| int rc = dt_statfs(NULL, dt, &osfs); |
| |
| if (rc == 0) { |
| __u32 blk_size = osfs.os_bsize >> 10; |
| __u64 result = osfs.os_blocks; |
| |
| while (blk_size >>= 1) |
| result <<= 1; |
| |
| *eof = 1; |
| rc = snprintf(page, count, "%llu\n", result); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(lprocfs_dt_rd_kbytestotal); |
| |
| int lprocfs_dt_rd_kbytesfree(char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct dt_device *dt = data; |
| struct obd_statfs osfs; |
| int rc = dt_statfs(NULL, dt, &osfs); |
| |
| if (rc == 0) { |
| __u32 blk_size = osfs.os_bsize >> 10; |
| __u64 result = osfs.os_bfree; |
| |
| while (blk_size >>= 1) |
| result <<= 1; |
| |
| *eof = 1; |
| rc = snprintf(page, count, "%llu\n", result); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(lprocfs_dt_rd_kbytesfree); |
| |
| int lprocfs_dt_rd_kbytesavail(char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct dt_device *dt = data; |
| struct obd_statfs osfs; |
| int rc = dt_statfs(NULL, dt, &osfs); |
| |
| if (rc == 0) { |
| __u32 blk_size = osfs.os_bsize >> 10; |
| __u64 result = osfs.os_bavail; |
| |
| while (blk_size >>= 1) |
| result <<= 1; |
| |
| *eof = 1; |
| rc = snprintf(page, count, "%llu\n", result); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(lprocfs_dt_rd_kbytesavail); |
| |
| int lprocfs_dt_rd_filestotal(char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct dt_device *dt = data; |
| struct obd_statfs osfs; |
| int rc = dt_statfs(NULL, dt, &osfs); |
| |
| if (rc == 0) { |
| *eof = 1; |
| rc = snprintf(page, count, "%llu\n", osfs.os_files); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(lprocfs_dt_rd_filestotal); |
| |
| int lprocfs_dt_rd_filesfree(char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct dt_device *dt = data; |
| struct obd_statfs osfs; |
| int rc = dt_statfs(NULL, dt, &osfs); |
| |
| if (rc == 0) { |
| *eof = 1; |
| rc = snprintf(page, count, "%llu\n", osfs.os_ffree); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(lprocfs_dt_rd_filesfree); |
| |
| #endif /* CONFIG_PROC_FS */ |