struct dt_device *dev);
/**
+ * Wait pending quota update finish
+ *
+ * There might be a window that quota usage has been updated,
+ * but commit callback to reduce pending write have not been
+ * finished, this is used to wait all pending update done.
+ *
+ * \param[in] dev dt device
+ */
+ void (*dt_wait_quota_pending)(struct dt_device *dev);
+
+ /**
* Start transaction commit asynchronously.
*
return dev->dd_ops->dt_ro(env, dev);
}
+static inline void dt_wait_quota_pending(struct dt_device *dev)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ if (dev->dd_ops->dt_wait_quota_pending)
+ dev->dd_ops->dt_wait_quota_pending(dev);
+}
+
static inline int dt_declare_insert(const struct lu_env *env,
struct dt_object *dt,
const struct dt_rec *rec,
RETURN(0);
}
+static void osd_wait_quota_pending(struct dt_device *d)
+{
+ struct osd_device *o = osd_dt_dev(d);
+
+ if (o->od_quota_slave_md != NULL ||
+ o->od_quota_slave_dt != NULL)
+ txg_wait_callbacks(spa_get_dsl(dmu_objset_spa(o->od_os)));
+}
+
static struct dt_device_operations osd_dt_ops = {
.dt_root_get = osd_root_get,
.dt_statfs = osd_statfs,
.dt_sync = osd_sync,
.dt_commit_async = osd_commit_async,
.dt_ro = osd_ro,
+ .dt_wait_quota_pending = osd_wait_quota_pending,
};
/*
long long space, int *ret)
{
int rc = 0, count;
+ int wait_pending = 0;
+ struct qsd_qtype_info *qqi = lqe2qqi(lqe);
+
ENTRY;
for (count = 0; rc == 0; count++) {
LQUOTA_DEBUG(lqe, "acquiring:%lld count=%d", space, count);
-
+again:
if (lqe2qqi(lqe)->qqi_qsd->qsd_stopping) {
rc = -EINPROGRESS;
break;
/* rc == 0, Wouhou! enough local quota space
* rc < 0, something bad happened */
break;
+ /*
+ * There might be a window that commit transaction
+ * have updated usage but pending write doesn't change
+ * wait for it before acquiring remotely.
+ */
+ if (lqe->lqe_pending_write >= space && !wait_pending) {
+ wait_pending = 1;
+ dt_wait_quota_pending(qqi->qqi_qsd->qsd_dev);
+ goto again;
+ }
/* if we have gotten some quota and stil wait more quota,
* it's better to give QMT some time to reclaim from clients */
local LIMIT=$2
local grace=$3
local qtype=$4
+ local SOFT_LIMIT=$(do_facet $SINGLEMDS $LCTL get_param -n \
+ qmt.$FSNAME-QMT0000.md-0x0.soft_least_qunit)
setup_quota_test
trap cleanup_quota_test EXIT
$SHOW_QUOTA_INFO_PROJID
echo "Create file after timer goes off"
- # There is a window that space is accounted in the quota usage but
- # hasn't been decreased from the pending write, if we acquire quota
- # in this window, we'll acquire more than we needed.
- $RUNAS touch ${TESTFILE}_after_1 ${TESTFILE}_after_2 || true
- sync_all_data || true
- $RUNAS touch ${TESTFILE}_after_3 &&
+ # exceed least soft limit is possible
+ $RUNAS createmany -m ${TESTFILE}_after_3 $((SOFT_LIMIT + 1)) &&
quota_error a $TSTUSR "create after timer expired," \
"but expect EDQUOT"
sync_all_data || true