Commit 64d608db authored by Jiri Slaby's avatar Jiri Slaby Committed by Greg Kroah-Hartman
Browse files

tty: cumulate and document tty_struct::ctrl* members



Group the ctrl members under a single struct called ctrl. The new struct
contains 'pgrp', 'session', 'pktstatus', and 'packet'. 'pktstatus' and
'packet' used to be bits in a bitfield. The struct also contains the
lock protecting them to share the same cache line.

Note that commit c545b66c (tty: Serialize tcflow() with other tty
flow control changes) added a padding to the original bitfield. It was
for the bitfield to occupy a whole 64b word to avoid interferring stores
on Alpha (cannot we evaporate this arch with weird implications to C
code yet?). But it doesn't work as expected as the padding
(tty_struct::ctrl_unused) is aligned to a 8B boundary too and occupies
some bytes from the next word.

So make it reliable by:
1) setting __aligned of the struct -- that aligns the start, and
2) making 'unsigned long unused[0]' as the last member of the struct --
   pads the end.

Add a kerneldoc comment for this grouped members.

Signed-off-by: default avatarJiri Slaby <jslaby@suse.cz>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: netdev@vger.kernel.org
Link: https://lore.kernel.org/r/20210505091928.22010-14-jslaby@suse.cz


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6e94dbc7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ static inline void update_tty_status(struct ser_device *ser)
	ser->tty_status =
		ser->tty->flow.stopped << 5 |
		ser->tty->flow.tco_stopped << 3 |
		ser->tty->packet << 2;
		ser->tty->ctrl.packet << 2;
}
static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
{
+15 −15
Original line number Diff line number Diff line
@@ -342,10 +342,10 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
{
	unsigned long flags;

	if (tty->link->packet) {
		spin_lock_irqsave(&tty->ctrl_lock, flags);
		tty->ctrl_status |= TIOCPKT_FLUSHREAD;
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	if (tty->link->ctrl.packet) {
		spin_lock_irqsave(&tty->ctrl.lock, flags);
		tty->ctrl.pktstatus |= TIOCPKT_FLUSHREAD;
		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
		wake_up_interruptible(&tty->link->read_wait);
	}
}
@@ -361,7 +361,7 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
 *	Holds termios_rwsem to exclude producer/consumer while
 *	buffer indices are reset.
 *
 *	Locking: ctrl_lock, exclusive termios_rwsem
 *	Locking: ctrl.lock, exclusive termios_rwsem
 */

static void n_tty_flush_buffer(struct tty_struct *tty)
@@ -1103,7 +1103,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 *	buffer is 'output'. The signal is processed first to alert any current
 *	readers or writers to discontinue and exit their i/o loops.
 *
 *	Locking: ctrl_lock
 *	Locking: ctrl.lock
 */

static void __isig(int sig, struct tty_struct *tty)
@@ -2025,7 +2025,7 @@ static bool canon_copy_from_read_buf(struct tty_struct *tty,
 *
 *	Locking: redirected write test is safe
 *		 current->signal->tty check is safe
 *		 ctrl_lock to safely reference tty->pgrp
 *		 ctrl.lock to safely reference tty->ctrl.pgrp
 */

static int job_control(struct tty_struct *tty, struct file *file)
@@ -2072,7 +2072,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
	int minimum, time;
	ssize_t retval = 0;
	long timeout;
	int packet;
	bool packet;
	size_t tail;

	/*
@@ -2128,20 +2128,20 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
		}
	}

	packet = tty->packet;
	packet = tty->ctrl.packet;
	tail = ldata->read_tail;

	add_wait_queue(&tty->read_wait, &wait);
	while (nr) {
		/* First test for status change. */
		if (packet && tty->link->ctrl_status) {
		if (packet && tty->link->ctrl.pktstatus) {
			unsigned char cs;
			if (kb != kbuf)
				break;
			spin_lock_irq(&tty->link->ctrl_lock);
			cs = tty->link->ctrl_status;
			tty->link->ctrl_status = 0;
			spin_unlock_irq(&tty->link->ctrl_lock);
			spin_lock_irq(&tty->link->ctrl.lock);
			cs = tty->link->ctrl.pktstatus;
			tty->link->ctrl.pktstatus = 0;
			spin_unlock_irq(&tty->link->ctrl.lock);
			*kb++ = cs;
			nr--;
			break;
@@ -2368,7 +2368,7 @@ static __poll_t n_tty_poll(struct tty_struct *tty, struct file *file,
		if (input_available_p(tty, 1))
			mask |= EPOLLIN | EPOLLRDNORM;
	}
	if (tty->packet && tty->link->ctrl_status)
	if (tty->ctrl.packet && tty->link->ctrl.pktstatus)
		mask |= EPOLLPRI | EPOLLIN | EPOLLRDNORM;
	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
		mask |= EPOLLHUP;
+31 −31
Original line number Diff line number Diff line
@@ -57,9 +57,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
	set_bit(TTY_IO_ERROR, &tty->flags);
	wake_up_interruptible(&tty->read_wait);
	wake_up_interruptible(&tty->write_wait);
	spin_lock_irq(&tty->ctrl_lock);
	tty->packet = 0;
	spin_unlock_irq(&tty->ctrl_lock);
	spin_lock_irq(&tty->ctrl.lock);
	tty->ctrl.packet = false;
	spin_unlock_irq(&tty->ctrl.lock);
	/* Review - krefs on tty_link ?? */
	if (!tty->link)
		return;
@@ -185,16 +185,16 @@ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
	if (get_user(pktmode, arg))
		return -EFAULT;

	spin_lock_irq(&tty->ctrl_lock);
	spin_lock_irq(&tty->ctrl.lock);
	if (pktmode) {
		if (!tty->packet) {
			tty->link->ctrl_status = 0;
		if (!tty->ctrl.packet) {
			tty->link->ctrl.pktstatus = 0;
			smp_mb();
			tty->packet = 1;
			tty->ctrl.packet = true;
		}
	} else
		tty->packet = 0;
	spin_unlock_irq(&tty->ctrl_lock);
		tty->ctrl.packet = false;
	spin_unlock_irq(&tty->ctrl.lock);

	return 0;
}
@@ -202,7 +202,7 @@ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
/* Get the packet mode of a pty */
static int pty_get_pktmode(struct tty_struct *tty, int __user *arg)
{
	int pktmode = tty->packet;
	int pktmode = tty->ctrl.packet;

	return put_user(pktmode, arg);
}
@@ -232,11 +232,11 @@ static void pty_flush_buffer(struct tty_struct *tty)
		return;

	tty_buffer_flush(to, NULL);
	if (to->packet) {
		spin_lock_irq(&tty->ctrl_lock);
		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
	if (to->ctrl.packet) {
		spin_lock_irq(&tty->ctrl.lock);
		tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;
		wake_up_interruptible(&to->read_wait);
		spin_unlock_irq(&tty->ctrl_lock);
		spin_unlock_irq(&tty->ctrl.lock);
	}
}

@@ -266,7 +266,7 @@ static void pty_set_termios(struct tty_struct *tty,
					struct ktermios *old_termios)
{
	/* See if packet mode change of state. */
	if (tty->link && tty->link->packet) {
	if (tty->link && tty->link->ctrl.packet) {
		int extproc = (old_termios->c_lflag & EXTPROC) | L_EXTPROC(tty);
		int old_flow = ((old_termios->c_iflag & IXON) &&
				(old_termios->c_cc[VSTOP] == '\023') &&
@@ -275,17 +275,17 @@ static void pty_set_termios(struct tty_struct *tty,
				STOP_CHAR(tty) == '\023' &&
				START_CHAR(tty) == '\021');
		if ((old_flow != new_flow) || extproc) {
			spin_lock_irq(&tty->ctrl_lock);
			spin_lock_irq(&tty->ctrl.lock);
			if (old_flow != new_flow) {
				tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
				tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
				if (new_flow)
					tty->ctrl_status |= TIOCPKT_DOSTOP;
					tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
				else
					tty->ctrl_status |= TIOCPKT_NOSTOP;
					tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;
			}
			if (extproc)
				tty->ctrl_status |= TIOCPKT_IOCTL;
			spin_unlock_irq(&tty->ctrl_lock);
				tty->ctrl.pktstatus |= TIOCPKT_IOCTL;
			spin_unlock_irq(&tty->ctrl.lock);
			wake_up_interruptible(&tty->link->read_wait);
		}
	}
@@ -346,11 +346,11 @@ static void pty_start(struct tty_struct *tty)
{
	unsigned long flags;

	if (tty->link && tty->link->packet) {
		spin_lock_irqsave(&tty->ctrl_lock, flags);
		tty->ctrl_status &= ~TIOCPKT_STOP;
		tty->ctrl_status |= TIOCPKT_START;
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	if (tty->link && tty->link->ctrl.packet) {
		spin_lock_irqsave(&tty->ctrl.lock, flags);
		tty->ctrl.pktstatus &= ~TIOCPKT_STOP;
		tty->ctrl.pktstatus |= TIOCPKT_START;
		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
		wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
	}
}
@@ -359,11 +359,11 @@ static void pty_stop(struct tty_struct *tty)
{
	unsigned long flags;

	if (tty->link && tty->link->packet) {
		spin_lock_irqsave(&tty->ctrl_lock, flags);
		tty->ctrl_status &= ~TIOCPKT_START;
		tty->ctrl_status |= TIOCPKT_STOP;
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	if (tty->link && tty->link->ctrl.packet) {
		spin_lock_irqsave(&tty->ctrl.lock, flags);
		tty->ctrl.pktstatus &= ~TIOCPKT_START;
		tty->ctrl.pktstatus |= TIOCPKT_STOP;
		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
		wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
	}
}
+22 −22
Original line number Diff line number Diff line
@@ -638,15 +638,15 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)

	tty_ldisc_hangup(tty, cons_filp != NULL);

	spin_lock_irq(&tty->ctrl_lock);
	spin_lock_irq(&tty->ctrl.lock);
	clear_bit(TTY_THROTTLED, &tty->flags);
	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
	put_pid(tty->session);
	put_pid(tty->pgrp);
	tty->session = NULL;
	tty->pgrp = NULL;
	tty->ctrl_status = 0;
	spin_unlock_irq(&tty->ctrl_lock);
	put_pid(tty->ctrl.session);
	put_pid(tty->ctrl.pgrp);
	tty->ctrl.session = NULL;
	tty->ctrl.pgrp = NULL;
	tty->ctrl.pktstatus = 0;
	spin_unlock_irq(&tty->ctrl.lock);

	/*
	 * If one of the devices matches a console pointer, we
@@ -1559,8 +1559,8 @@ static void release_one_tty(struct work_struct *work)
	list_del_init(&tty->tty_files);
	spin_unlock(&tty->files_lock);

	put_pid(tty->pgrp);
	put_pid(tty->session);
	put_pid(tty->ctrl.pgrp);
	put_pid(tty->ctrl.session);
	free_tty_struct(tty);
}

@@ -1861,9 +1861,9 @@ int tty_release(struct inode *inode, struct file *filp)
	 */
	if (!tty->count) {
		read_lock(&tasklist_lock);
		session_clear_tty(tty->session);
		session_clear_tty(tty->ctrl.session);
		if (o_tty)
			session_clear_tty(o_tty->session);
			session_clear_tty(o_tty->ctrl.session);
		read_unlock(&tasklist_lock);
	}

@@ -2250,16 +2250,16 @@ static int __tty_fasync(int fd, struct file *filp, int on)
		enum pid_type type;
		struct pid *pid;

		spin_lock_irqsave(&tty->ctrl_lock, flags);
		if (tty->pgrp) {
			pid = tty->pgrp;
		spin_lock_irqsave(&tty->ctrl.lock, flags);
		if (tty->ctrl.pgrp) {
			pid = tty->ctrl.pgrp;
			type = PIDTYPE_PGID;
		} else {
			pid = task_pid(current);
			type = PIDTYPE_TGID;
		}
		get_pid(pid);
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
		__f_setown(filp, pid, type, 0);
		put_pid(pid);
		retval = 0;
@@ -2381,7 +2381,7 @@ EXPORT_SYMBOL(tty_do_resize);
 *
 *	Locking:
 *		Driver dependent. The default do_resize method takes the
 *	tty termios mutex and ctrl_lock. The console takes its own lock
 *	tty termios mutex and ctrl.lock. The console takes its own lock
 *	then calls into the default method.
 */

@@ -3039,9 +3039,9 @@ void __do_SAK(struct tty_struct *tty)
	if (!tty)
		return;

	spin_lock_irqsave(&tty->ctrl_lock, flags);
	session = get_pid(tty->session);
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	spin_lock_irqsave(&tty->ctrl.lock, flags);
	session = get_pid(tty->ctrl.session);
	spin_unlock_irqrestore(&tty->ctrl.lock, flags);

	tty_ldisc_flush(tty);

@@ -3129,8 +3129,8 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
		kfree(tty);
		return NULL;
	}
	tty->session = NULL;
	tty->pgrp = NULL;
	tty->ctrl.session = NULL;
	tty->ctrl.pgrp = NULL;
	mutex_init(&tty->legacy_mutex);
	mutex_init(&tty->throttle_mutex);
	init_rwsem(&tty->termios_rwsem);
@@ -3140,7 +3140,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
	init_waitqueue_head(&tty->read_wait);
	INIT_WORK(&tty->hangup_work, do_tty_hangup);
	mutex_init(&tty->atomic_write_lock);
	spin_lock_init(&tty->ctrl_lock);
	spin_lock_init(&tty->ctrl.lock);
	spin_lock_init(&tty->flow.lock);
	spin_lock_init(&tty->files_lock);
	INIT_LIST_HEAD(&tty->tty_files);
+43 −41
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ static int is_ignored(int sig)
 *	not in the foreground, send a SIGTTOU.  If the signal is blocked or
 *	ignored, go ahead and perform the operation.  (POSIX 7.2)
 *
 *	Locking: ctrl_lock
 *	Locking: ctrl.lock
 */
int __tty_check_change(struct tty_struct *tty, int sig)
{
@@ -42,9 +42,9 @@ int __tty_check_change(struct tty_struct *tty, int sig)
	rcu_read_lock();
	pgrp = task_pgrp(current);

	spin_lock_irqsave(&tty->ctrl_lock, flags);
	tty_pgrp = tty->pgrp;
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	spin_lock_irqsave(&tty->ctrl.lock, flags);
	tty_pgrp = tty->ctrl.pgrp;
	spin_unlock_irqrestore(&tty->ctrl.lock, flags);

	if (tty_pgrp && pgrp != tty_pgrp) {
		if (is_ignored(sig)) {
@@ -99,16 +99,16 @@ static void __proc_set_tty(struct tty_struct *tty)
{
	unsigned long flags;

	spin_lock_irqsave(&tty->ctrl_lock, flags);
	spin_lock_irqsave(&tty->ctrl.lock, flags);
	/*
	 * The session and fg pgrp references will be non-NULL if
	 * tiocsctty() is stealing the controlling tty
	 */
	put_pid(tty->session);
	put_pid(tty->pgrp);
	tty->pgrp = get_pid(task_pgrp(current));
	tty->session = get_pid(task_session(current));
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	put_pid(tty->ctrl.session);
	put_pid(tty->ctrl.pgrp);
	tty->ctrl.pgrp = get_pid(task_pgrp(current));
	tty->ctrl.session = get_pid(task_session(current));
	spin_unlock_irqrestore(&tty->ctrl.lock, flags);
	if (current->signal->tty) {
		tty_debug(tty, "current tty %s not NULL!!\n",
			  current->signal->tty->name);
@@ -135,7 +135,7 @@ void tty_open_proc_set_tty(struct file *filp, struct tty_struct *tty)
	spin_lock_irq(&current->sighand->siglock);
	if (current->signal->leader &&
	    !current->signal->tty &&
	    tty->session == NULL) {
	    tty->ctrl.session == NULL) {
		/*
		 * Don't let a process that only has write access to the tty
		 * obtain the privileges associated with having a tty as
@@ -200,8 +200,8 @@ int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
	struct pid *tty_pgrp = NULL;

	read_lock(&tasklist_lock);
	if (tty->session) {
		do_each_pid_task(tty->session, PIDTYPE_SID, p) {
	if (tty->ctrl.session) {
		do_each_pid_task(tty->ctrl.session, PIDTYPE_SID, p) {
			spin_lock_irq(&p->sighand->siglock);
			if (p->signal->tty == tty) {
				p->signal->tty = NULL;
@@ -218,13 +218,14 @@ int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
			__group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
			__group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
			put_pid(p->signal->tty_old_pgrp);  /* A noop */
			spin_lock(&tty->ctrl_lock);
			tty_pgrp = get_pid(tty->pgrp);
			if (tty->pgrp)
				p->signal->tty_old_pgrp = get_pid(tty->pgrp);
			spin_unlock(&tty->ctrl_lock);
			spin_lock(&tty->ctrl.lock);
			tty_pgrp = get_pid(tty->ctrl.pgrp);
			if (tty->ctrl.pgrp)
				p->signal->tty_old_pgrp =
					get_pid(tty->ctrl.pgrp);
			spin_unlock(&tty->ctrl.lock);
			spin_unlock_irq(&p->sighand->siglock);
		} while_each_pid_task(tty->session, PIDTYPE_SID, p);
		} while_each_pid_task(tty->ctrl.session, PIDTYPE_SID, p);
	}
	read_unlock(&tasklist_lock);

@@ -309,12 +310,12 @@ void disassociate_ctty(int on_exit)
		unsigned long flags;

		tty_lock(tty);
		spin_lock_irqsave(&tty->ctrl_lock, flags);
		put_pid(tty->session);
		put_pid(tty->pgrp);
		tty->session = NULL;
		tty->pgrp = NULL;
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
		spin_lock_irqsave(&tty->ctrl.lock, flags);
		put_pid(tty->ctrl.session);
		put_pid(tty->ctrl.pgrp);
		tty->ctrl.session = NULL;
		tty->ctrl.pgrp = NULL;
		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
		tty_unlock(tty);
		tty_kref_put(tty);
	}
@@ -363,7 +364,8 @@ static int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
	tty_lock(tty);
	read_lock(&tasklist_lock);

	if (current->signal->leader && (task_session(current) == tty->session))
	if (current->signal->leader &&
			task_session(current) == tty->ctrl.session)
		goto unlock;

	/*
@@ -375,7 +377,7 @@ static int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
		goto unlock;
	}

	if (tty->session) {
	if (tty->ctrl.session) {
		/*
		 * This tty is already the controlling
		 * tty for another session group!
@@ -384,7 +386,7 @@ static int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
			/*
			 * Steal it away
			 */
			session_clear_tty(tty->session);
			session_clear_tty(tty->ctrl.session);
		} else {
			ret = -EPERM;
			goto unlock;
@@ -416,9 +418,9 @@ struct pid *tty_get_pgrp(struct tty_struct *tty)
	unsigned long flags;
	struct pid *pgrp;

	spin_lock_irqsave(&tty->ctrl_lock, flags);
	pgrp = get_pid(tty->pgrp);
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	spin_lock_irqsave(&tty->ctrl.lock, flags);
	pgrp = get_pid(tty->ctrl.pgrp);
	spin_unlock_irqrestore(&tty->ctrl.lock, flags);

	return pgrp;
}
@@ -499,10 +501,10 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
	if (pgrp_nr < 0)
		return -EINVAL;

	spin_lock_irq(&real_tty->ctrl_lock);
	spin_lock_irq(&real_tty->ctrl.lock);
	if (!current->signal->tty ||
	    (current->signal->tty != real_tty) ||
	    (real_tty->session != task_session(current))) {
	    (real_tty->ctrl.session != task_session(current))) {
		retval = -ENOTTY;
		goto out_unlock_ctrl;
	}
@@ -515,12 +517,12 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
	if (session_of_pgrp(pgrp) != task_session(current))
		goto out_unlock;
	retval = 0;
	put_pid(real_tty->pgrp);
	real_tty->pgrp = get_pid(pgrp);
	put_pid(real_tty->ctrl.pgrp);
	real_tty->ctrl.pgrp = get_pid(pgrp);
out_unlock:
	rcu_read_unlock();
out_unlock_ctrl:
	spin_unlock_irq(&real_tty->ctrl_lock);
	spin_unlock_irq(&real_tty->ctrl.lock);
	return retval;
}

@@ -545,16 +547,16 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _
	if (tty == real_tty && current->signal->tty != real_tty)
		return -ENOTTY;

	spin_lock_irqsave(&real_tty->ctrl_lock, flags);
	if (!real_tty->session)
	spin_lock_irqsave(&real_tty->ctrl.lock, flags);
	if (!real_tty->ctrl.session)
		goto err;
	sid = pid_vnr(real_tty->session);
	spin_unlock_irqrestore(&real_tty->ctrl_lock, flags);
	sid = pid_vnr(real_tty->ctrl.session);
	spin_unlock_irqrestore(&real_tty->ctrl.lock, flags);

	return put_user(sid, p);

err:
	spin_unlock_irqrestore(&real_tty->ctrl_lock, flags);
	spin_unlock_irqrestore(&real_tty->ctrl.lock, flags);
	return -ENOTTY;
}

Loading