#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_quota.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/file.h>
#include <sys/fs/ufs_panic.h>
#include <sys/var.h>
struct dqhead dqhead[NDQHASH];
static kmutex_t dq_cachelock;
static kmutex_t dq_freelock;
krwlock_t dq_rwlock;
struct dquot dqfreelist;
#define dqinsheadfree(DQP) { \
mutex_enter(&dq_freelock); \
(DQP)->dq_freef = dqfreelist.dq_freef; \
(DQP)->dq_freeb = &dqfreelist; \
dqfreelist.dq_freef->dq_freeb = (DQP); \
dqfreelist.dq_freef = (DQP); \
mutex_exit(&dq_freelock); \
}
#define dqinstailfree(DQP) { \
mutex_enter(&dq_freelock); \
(DQP)->dq_freeb = dqfreelist.dq_freeb; \
(DQP)->dq_freef = &dqfreelist; \
dqfreelist.dq_freeb->dq_freef = (DQP); \
dqfreelist.dq_freeb = (DQP); \
mutex_exit(&dq_freelock); \
}
#define dqremfree(DQP) { \
(DQP)->dq_freeb->dq_freef = (DQP)->dq_freef; \
(DQP)->dq_freef->dq_freeb = (DQP)->dq_freeb; \
(DQP)->dq_freef = (DQP)->dq_freeb = NULL; \
}
typedef struct dquot *DQptr;
void
qtinit()
{
rw_init(&dq_rwlock, NULL, RW_DEFAULT, NULL);
}
void
qtinit2(void)
{
register struct dqhead *dhp;
register struct dquot *dqp;
ASSERT(RW_WRITE_HELD(&dq_rwlock));
if (ndquot == 0)
ndquot = ((maxusers * NMOUNT) / 4) + v.v_proc;
dquot = kmem_zalloc(ndquot * sizeof (struct dquot), KM_SLEEP);
dquotNDQUOT = dquot + ndquot;
for (dhp = &dqhead[0]; dhp < &dqhead[NDQHASH]; dhp++) {
dhp->dqh_forw = dhp->dqh_back = (DQptr)dhp;
}
dqfreelist.dq_freef = dqfreelist.dq_freeb = (DQptr)&dqfreelist;
for (dqp = dquot; dqp < dquotNDQUOT; dqp++) {
mutex_init(&dqp->dq_lock, NULL, MUTEX_DEFAULT, NULL);
dqp->dq_forw = dqp->dq_back = dqp;
dqinsheadfree(dqp);
}
}
int
getdiskquota(
uid_t uid,
struct ufsvfs *ufsvfsp,
int force,
struct dquot **dqpp)
{
struct dquot *dqp;
struct dqhead *dhp;
struct inode *qip;
int error;
extern struct cred *kcred;
daddr_t bn;
int contig;
int err;
ASSERT(RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
dhp = &dqhead[DQHASH(uid, ufsvfsp)];
loop:
if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0 && !force)
return (ESRCH);
qip = ufsvfsp->vfs_qinod;
if (!qip)
return (ufs_fault(ufsvfsp->vfs_root, "getdiskquota: NULL qip"));
mutex_enter(&dq_cachelock);
for (dqp = dhp->dqh_forw; dqp != (DQptr)dhp; dqp = dqp->dq_forw) {
if (dqp->dq_uid != uid || dqp->dq_ufsvfsp != ufsvfsp)
continue;
mutex_exit(&dq_cachelock);
mutex_enter(&dqp->dq_lock);
if (dqp->dq_uid != uid || dqp->dq_ufsvfsp != ufsvfsp) {
mutex_exit(&dqp->dq_lock);
goto loop;
}
if (dqp->dq_flags & DQ_ERROR) {
mutex_exit(&dqp->dq_lock);
return (EINVAL);
}
if (dqp->dq_cnt == 0) {
mutex_enter(&dq_freelock);
dqremfree(dqp);
mutex_exit(&dq_freelock);
}
dqp->dq_cnt++;
mutex_exit(&dqp->dq_lock);
*dqpp = dqp;
return (0);
}
mutex_enter(&dq_freelock);
if ((dqp = dqfreelist.dq_freef) == &dqfreelist) {
mutex_exit(&dq_freelock);
mutex_exit(&dq_cachelock);
cmn_err(CE_WARN, "dquot table full");
return (EUSERS);
}
if (dqp->dq_cnt != 0 || dqp->dq_flags != 0) {
panic("getdiskquota: dqp->dq_cnt: "
"%ld != 0 || dqp->dq_flags: 0x%x != 0 (%s)",
dqp->dq_cnt, dqp->dq_flags, qip->i_fs->fs_fsmnt);
}
dqremfree(dqp);
mutex_exit(&dq_freelock);
remque(dqp);
dqp->dq_cnt = 1;
dqp->dq_uid = uid;
dqp->dq_ufsvfsp = ufsvfsp;
dqp->dq_mof = UFS_HOLE;
mutex_enter(&dqp->dq_lock);
insque(dqp, dhp);
mutex_exit(&dq_cachelock);
rw_enter(&qip->i_contents, RW_READER);
if (uid <= MAXUID && dqoff(uid) >= 0 && dqoff(uid) < qip->i_size) {
error = ufs_rdwri(UIO_READ, FREAD, qip, (caddr_t)&dqp->dq_dqb,
sizeof (struct dqblk), dqoff(uid), UIO_SYSSPACE,
(int *)NULL, kcred);
err = bmap_read(qip, dqoff(uid), &bn, &contig);
rw_exit(&qip->i_contents);
if ((bn != UFS_HOLE) && !err) {
dqp->dq_mof = ldbtob(bn) +
(offset_t)(dqoff(uid) & (DEV_BSIZE - 1));
} else {
dqp->dq_mof = UFS_HOLE;
}
if (error) {
dqp->dq_flags = DQ_ERROR;
mutex_exit(&dqp->dq_lock);
mutex_enter(&dq_cachelock);
mutex_enter(&dqp->dq_lock);
remque(dqp);
mutex_exit(&dqp->dq_lock);
mutex_exit(&dq_cachelock);
dqp->dq_cnt = 0;
dqp->dq_ufsvfsp = NULL;
dqp->dq_forw = dqp;
dqp->dq_back = dqp;
dqp->dq_mof = UFS_HOLE;
dqp->dq_flags = 0;
dqinsheadfree(dqp);
return (EIO);
}
} else {
rw_exit(&qip->i_contents);
bzero(&dqp->dq_dqb, sizeof (struct dqblk));
dqp->dq_mof = UFS_HOLE;
}
mutex_exit(&dqp->dq_lock);
*dqpp = dqp;
return (0);
}
void
dqput(dqp)
register struct dquot *dqp;
{
ASSERT(dqp->dq_ufsvfsp == NULL ||
RW_LOCK_HELD(&dqp->dq_ufsvfsp->vfs_dqrwlock));
ASSERT(MUTEX_HELD(&dqp->dq_lock));
if (dqp->dq_cnt == 0) {
(void) ufs_fault(
dqp->dq_ufsvfsp && dqp->dq_ufsvfsp->vfs_root?
dqp->dq_ufsvfsp->vfs_root: NULL,
"dqput: dqp->dq_cnt == 0");
return;
}
if (--dqp->dq_cnt == 0) {
if (dqp->dq_flags & DQ_MOD)
dqupdate(dqp);
dqp->dq_flags = 0;
if (dqp->dq_ufsvfsp == NULL ||
dqp->dq_ufsvfsp->vfs_qflags == 0) {
dqinval(dqp);
} else
dqinstailfree(dqp);
}
}
void
dqupdate(dqp)
register struct dquot *dqp;
{
register struct inode *qip;
extern struct cred *kcred;
struct ufsvfs *ufsvfsp;
int newtrans = 0;
struct vnode *vfs_root;
ASSERT(MUTEX_HELD(&dqp->dq_lock));
if (!dqp->dq_ufsvfsp) {
(void) ufs_fault(NULL, "dqupdate: NULL dq_ufsvfsp");
return;
}
vfs_root = dqp->dq_ufsvfsp->vfs_root;
if (!vfs_root) {
(void) ufs_fault(NULL, "dqupdate: NULL vfs_root");
return;
}
qip = dqp->dq_ufsvfsp->vfs_qinod;
if (!qip) {
(void) ufs_fault(vfs_root, "dqupdate: NULL vfs_qinod");
return;
}
ufsvfsp = qip->i_ufsvfs;
if (!ufsvfsp) {
(void) ufs_fault(vfs_root,
"dqupdate: NULL vfs_qinod->i_ufsvfs");
return;
}
if (ufsvfsp != dqp->dq_ufsvfsp) {
(void) ufs_fault(vfs_root,
"dqupdate: vfs_qinod->i_ufsvfs != dqp->dq_ufsvfsp");
return;
}
if (!(dqp->dq_flags & DQ_MOD)) {
(void) ufs_fault(vfs_root,
"dqupdate: !(dqp->dq_flags & DQ_MOD)");
return;
}
if (!(curthread->t_flag & T_DONTBLOCK)) {
newtrans++;
curthread->t_flag |= T_DONTBLOCK;
TRANS_BEGIN_ASYNC(ufsvfsp, TOP_QUOTA, TOP_QUOTA_SIZE);
}
if (TRANS_ISTRANS(ufsvfsp)) {
TRANS_DELTA(ufsvfsp, dqp->dq_mof, sizeof (struct dqblk),
DT_QR, 0, 0);
TRANS_LOG(ufsvfsp, (caddr_t)&dqp->dq_dqb, dqp->dq_mof,
(int)(sizeof (struct dqblk)), NULL, 0);
} else {
rw_enter(&qip->i_contents, RW_WRITER);
if (dqoff(dqp->dq_uid) >= 0) {
(void) ufs_rdwri(UIO_WRITE, FWRITE, qip,
(caddr_t)&dqp->dq_dqb,
sizeof (struct dqblk),
dqoff(dqp->dq_uid), UIO_SYSSPACE,
(int *)NULL, kcred);
}
rw_exit(&qip->i_contents);
}
dqp->dq_flags &= ~DQ_MOD;
if (newtrans) {
TRANS_END_ASYNC(ufsvfsp, TOP_QUOTA, TOP_QUOTA_SIZE);
curthread->t_flag &= ~T_DONTBLOCK;
}
}
void
dqinval(dqp)
register struct dquot *dqp;
{
ASSERT(MUTEX_HELD(&dqp->dq_lock));
ASSERT(dqp->dq_cnt == 0);
ASSERT(dqp->dq_flags == 0);
ASSERT(dqp->dq_freef == NULL && dqp->dq_freeb == NULL);
ASSERT(dqp->dq_ufsvfsp &&
(dqp->dq_ufsvfsp->vfs_qflags & MQ_ENABLED) == 0);
dqp->dq_ufsvfsp = NULL;
mutex_exit(&dqp->dq_lock);
mutex_enter(&dq_cachelock);
mutex_enter(&dqp->dq_lock);
ASSERT(dqp->dq_cnt == 0);
ASSERT(dqp->dq_flags == 0);
ASSERT(dqp->dq_freef == NULL && dqp->dq_freeb == NULL);
ASSERT(dqp->dq_ufsvfsp == NULL);
remque(dqp);
mutex_exit(&dq_cachelock);
dqp->dq_forw = dqp;
dqp->dq_back = dqp;
dqinsheadfree(dqp);
}
void
invalidatedq(ufsvfsp)
register struct ufsvfs *ufsvfsp;
{
register struct dquot *dqp;
rw_enter(&dq_rwlock, RW_READER);
if (!quotas_initialized) {
rw_exit(&dq_rwlock);
return;
}
rw_exit(&dq_rwlock);
rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER);
ASSERT((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0);
for (dqp = dquot; dqp < dquotNDQUOT; dqp++) {
if (!mutex_tryenter(&dqp->dq_lock)) {
continue;
}
if (dqp->dq_ufsvfsp == ufsvfsp) {
ASSERT(dqp->dq_cnt == 0 || (dqp->dq_cnt == 1 &&
(dqp->dq_flags & DQ_TRANS)));
if (dqp->dq_cnt == 1 && (dqp->dq_flags & DQ_TRANS)) {
mutex_exit(&dqp->dq_lock);
continue;
}
ASSERT(dqp->dq_cnt == 0);
ASSERT(dqp->dq_freef && dqp->dq_freeb);
mutex_enter(&dq_freelock);
dqremfree(dqp);
mutex_exit(&dq_freelock);
dqinval(dqp);
}
mutex_exit(&dqp->dq_lock);
}
rw_exit(&ufsvfsp->vfs_dqrwlock);
}