Example #1
0
/**
 * Trigger pre-acquire/release if necessary.
 * It's only used by ldiskfs osd so far. When unlink a file in ldiskfs, the
 * quota accounting isn't updated when the transaction stopped. Instead, it'll
 * be updated on the final iput, so qsd_op_adjust() will be called then (in
 * osd_object_delete()) to trigger quota release if necessary.
 *
 * \param env - the environment passed by the caller
 * \param qsd - is the qsd instance associated with the device in charge
 *              of the operation.
 * \param qid - is the lquota ID of the user/group for which to trigger
 *              quota space adjustment
 * \param qtype - is the quota type (USRQUOTA or GRPQUOTA)
 */
void qsd_op_adjust(const struct lu_env *env, struct qsd_instance *qsd,
		   union lquota_id *qid, int qtype)
{
	struct lquota_entry    *lqe;
	struct qsd_qtype_info  *qqi;
	bool			adjust;
	ENTRY;

	if (unlikely(qsd == NULL))
		RETURN_EXIT;

	/* We don't enforce quota until the qsd_instance is started */
	read_lock(&qsd->qsd_lock);
	if (!qsd->qsd_started) {
		read_unlock(&qsd->qsd_lock);
		RETURN_EXIT;
	}
	read_unlock(&qsd->qsd_lock);

	qqi = qsd->qsd_type_array[qtype];
	LASSERT(qqi);

	if (!qsd_type_enabled(qsd, qtype) || qqi->qqi_acct_obj == NULL ||
	    qid->qid_uid == 0)
		RETURN_EXIT;

	read_lock(&qsd->qsd_lock);
	if (!qsd->qsd_started) {
		read_unlock(&qsd->qsd_lock);
		RETURN_EXIT;
	}
	read_unlock(&qsd->qsd_lock);

	lqe = lqe_locate(env, qqi->qqi_site, qid);
	if (IS_ERR(lqe)) {
		CERROR("%s: fail to locate lqe for id:"LPU64", type:%d\n",
		       qsd->qsd_svname, qid->qid_uid, qtype);
		RETURN_EXIT;
	}

	qsd_refresh_usage(env, lqe);

	lqe_read_lock(lqe);
	adjust = qsd_adjust_needed(lqe);
	lqe_read_unlock(lqe);

	if (adjust)
		qsd_adjust(env, lqe);

	lqe_putref(lqe);
	EXIT;
}
Example #2
0
/**
 * Post quota operation, pre-acquire/release quota from master.
 *
 * \param  env  - the environment passed by the caller
 * \param  qsd  - is the qsd instance attached to the OSD device which
 *                is handling the operation.
 * \param  qqi  - is the qsd_qtype_info structure associated with the quota ID
 *                subject to the operation
 * \param  qid  - stores information related to his ID for the operation
 *                which has just completed
 *
 * \retval 0    - success
 * \retval -ve  - failure
 */
static void qsd_op_end0(const struct lu_env *env, struct qsd_qtype_info *qqi,
			struct lquota_id_info *qid)
{
	struct lquota_entry	*lqe;
	bool			 adjust;
	ENTRY;

	lqe = qid->lqi_qentry;
	if (lqe == NULL)
		RETURN_EXIT;
	qid->lqi_qentry = NULL;

	/* refresh cached usage if a suitable environment is passed */
	if (env != NULL)
		qsd_refresh_usage(env, lqe);

	lqe_write_lock(lqe);
	if (qid->lqi_space > 0)
		lqe->lqe_pending_write -= qid->lqi_space;
	if (env != NULL)
		adjust = qsd_adjust_needed(lqe);
	else
		adjust = true;
	lqe_write_unlock(lqe);

	if (adjust) {
		/* pre-acquire/release quota space is needed */
		if (env != NULL)
			qsd_adjust(env, lqe);
		else
			/* no suitable environment, handle adjustment in
			 * separate thread context */
			qsd_adjust_schedule(lqe, false, false);
	}
	lqe_putref(lqe);
	EXIT;
}
Example #3
0
/**
 * Acquire \a space of quota space in order to complete an operation.
 * Try to consume local quota space first and send acquire request to quota
 * master if required.
 *
 * \param env   - the environment passed by the caller
 * \param lqe   - is the qid entry to be processed
 * \param space - is the amount of quota required for the operation
 * \param ret   - is the return code (-EDQUOT, -EINPROGRESS, ...)
 *
 * \retval true  - exit from l_wait_event and real return value in \a ret
 * \retval false - continue waiting
 */
static bool qsd_acquire(const struct lu_env *env, struct lquota_entry *lqe,
			long long space, int *ret)
{
	int rc = 0, count;
	ENTRY;

	for (count = 0; rc == 0; count++) {
		LQUOTA_DEBUG(lqe, "acquiring:"LPD64 " count=%d", space, count);

		if (lqe2qqi(lqe)->qqi_qsd->qsd_stopping) {
			rc = -EINPROGRESS;
			break;
		}

		/* refresh disk usage */
		rc = qsd_refresh_usage(env, lqe);
		if (rc)
			break;

		/* try to consume local quota space first */
		rc = qsd_acquire_local(lqe, space);
		if (rc != -EAGAIN)
			/* rc == 0, Wouhou! enough local quota space
			 * rc < 0, something bad happened */
			 break;

		/* need to acquire more quota space from master */
		rc = qsd_acquire_remote(env, lqe);
	}

	if (rc == -EBUSY)
		/* already a request in flight, continue waiting */
		RETURN(false);
	*ret = rc;
	RETURN(true); /* exit from l_wait_event */
}
Example #4
0
/*
 * Update a slave quota entry. This is done by reading enforcement status from
 * the copy of the global index and then how much is the slave currenly owns
 * for this user from the slave index copy.
 *
 * \param env - the environment passed by the caller
 * \param lqe - is the quota entry to refresh
 * \param arg - is the pointer to the qsd_qtype_info structure
 */
static int qsd_lqe_read(const struct lu_env *env, struct lquota_entry *lqe,
			void *arg)
{
	struct qsd_thread_info *qti = qsd_info(env);
	struct qsd_qtype_info  *qqi = (struct qsd_qtype_info *)arg;
	int			rc;

	LASSERT(!lqe_is_master(lqe));

	/* read record from global index copy to know whether quota is
	 * enforced for this user */
	rc = lquota_disk_read(env, qqi->qqi_glb_obj, &lqe->lqe_id,
			      (struct dt_rec *)&qti->qti_glb_rec);

	switch(rc) {
	case -ENOENT:
		/* no such entry, assume quota isn't enforced for this user */
		lqe->lqe_enforced = false;
		break;
	case 0:
		if (qti->qti_glb_rec.qbr_hardlimit == 0 &&
		    qti->qti_glb_rec.qbr_softlimit == 0)
			/* quota isn't enforced for this use */
			lqe->lqe_enforced = false;
		else
			lqe->lqe_enforced = true;
		break;
	default:
		LQUOTA_ERROR(lqe, "failed to read quota entry from global "
			     "index copy, rc:%d", rc);
		return rc;
	}

	/* read record from slave index copy to find out how much space is
	 * currently owned by this slave */
	rc = lquota_disk_read(env, qqi->qqi_slv_obj, &lqe->lqe_id,
			      (struct dt_rec *)&qti->qti_slv_rec);
	switch(rc) {
	case -ENOENT:
		lqe->lqe_granted = 0;
		break;
	case 0:
		lqe->lqe_granted = qti->qti_slv_rec.qsr_granted;
		break;
	default:
		LQUOTA_ERROR(lqe, "failed to read quota entry from slave "
			     "index copy, rc:%d", rc);
		return rc;
	}

	/* don't know what the qunit value is yet */
	qsd_set_qunit(lqe, 0);

	/* read current disk-usage from disk */
	rc = qsd_refresh_usage(env, lqe);
	if (rc)
		return rc;

	LQUOTA_DEBUG(lqe, "successfully read from disk");
	return 0;
}
Example #5
0
/**
 * Quota enforcement handler. If local quota can satisfy this operation,
 * return success, otherwise, acquire more quota from master.
 * (for write operation, if master isn't available at this moment, return
 * -EINPROGRESS to inform client to retry the write)
 *
 * \param env   - the environment passed by the caller
 * \param qsd   - is the qsd instance associated with the device in charge
 *                of the operation.
 * \param qid   - is the qid information attached in the transaction handle
 * \param space - is the space required by the operation
 * \param flags - if the operation is write, return caller no user/group
 *                and sync commit flags
 *
 * \retval 0            - success
 * \retval -EDQUOT      - out of quota
 * \retval -EINPROGRESS - inform client to retry write
 * \retval -ve          - other appropriate errors
 */
static int qsd_op_begin0(const struct lu_env *env, struct qsd_qtype_info *qqi,
			 struct lquota_id_info *qid, long long space,
			 int *flags)
{
	struct lquota_entry	*lqe;
	int			 rc, ret = -EINPROGRESS;
	struct l_wait_info	 lwi;
	ENTRY;

	if (qid->lqi_qentry != NULL) {
		/* we already had to deal with this id for this transaction */
		lqe = qid->lqi_qentry;
		if (!lqe->lqe_enforced)
			RETURN(0);
	} else {
		/* look up lquota entry associated with qid */
		lqe = lqe_locate(env, qqi->qqi_site, &qid->lqi_id);
		if (IS_ERR(lqe))
			RETURN(PTR_ERR(lqe));
		if (!lqe->lqe_enforced) {
			lqe_putref(lqe);
			RETURN(0);
		}
		qid->lqi_qentry = lqe;
		/* lqe will be released in qsd_op_end() */
	}

	if (space <= 0) {
		/* when space is negative or null, we don't need to consume
		 * quota space. That said, we still want to perform space
		 * adjustments in qsd_op_end, so we return here, but with
		 * a reference on the lqe */
		if (flags != NULL) {
			rc = qsd_refresh_usage(env, lqe);
			GOTO(out_flags, rc);
		}
		RETURN(0);
	}

	LQUOTA_DEBUG(lqe, "op_begin space:"LPD64, space);

	lqe_write_lock(lqe);
	lqe->lqe_waiting_write += space;
	lqe_write_unlock(lqe);

	/* acquire quota space for the operation, cap overall wait time to
	 * prevent a service thread from being stuck for too long */
	lwi = LWI_TIMEOUT(cfs_time_seconds(qsd_wait_timeout(qqi->qqi_qsd)),
			  NULL, NULL);
	rc = l_wait_event(lqe->lqe_waiters, qsd_acquire(env, lqe, space, &ret),
			  &lwi);

	if (rc == 0 && ret == 0) {
		qid->lqi_space += space;
	} else {
		if (rc == 0)
			rc = ret;

		LQUOTA_DEBUG(lqe, "acquire quota failed:%d", rc);

		lqe_write_lock(lqe);
		lqe->lqe_waiting_write -= space;

		if (flags && lqe->lqe_pending_write != 0)
			/* Inform OSD layer that there are pending writes.
			 * It might want to retry after a sync if appropriate */
			 *flags |= QUOTA_FL_SYNC;
		lqe_write_unlock(lqe);

		/* convert recoverable error into -EINPROGRESS, client will
		 * retry */
		if (rc == -ETIMEDOUT || rc == -ENOTCONN || rc == -ENOLCK ||
		    rc == -EAGAIN || rc == -EINTR) {
			rc = -EINPROGRESS;
		} else if (rc == -ESRCH) {
			rc = 0;
			LQUOTA_ERROR(lqe, "ID isn't enforced on master, it "
				     "probably due to a legeal race, if this "
				     "message is showing up constantly, there "
				     "could be some inconsistence between "
				     "master & slave, and quota reintegration "
				     "needs be re-triggered.");
		}
	}

	if (flags != NULL) {
out_flags:
		LASSERT(qid->lqi_is_blk);
		if (rc != 0) {
			*flags |= LQUOTA_OVER_FL(qqi->qqi_qtype);
		} else {
			__u64	usage;

			lqe_read_lock(lqe);
			usage  = lqe->lqe_usage;
			usage += lqe->lqe_pending_write;
			usage += lqe->lqe_waiting_write;
			usage += qqi->qqi_qsd->qsd_sync_threshold;

			/* if we should notify client to start sync write */
			if (usage >= lqe->lqe_granted - lqe->lqe_pending_rel)
				*flags |= LQUOTA_OVER_FL(qqi->qqi_qtype);
			else
				*flags &= ~LQUOTA_OVER_FL(qqi->qqi_qtype);
			lqe_read_unlock(lqe);
		}
	}
	RETURN(rc);
}