Skip to contentSkip to conversation

driver: ip27-rtc - convert ioctl to unlocked_ioctl

PATCH In review Cyrill Gorcunov 4 participants 14 messages Jan 13, 2008 – Jan 14, 2008 Char and misc drivers

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 driver: ip27-rtc - convert ioctl to unlocked_ioctl

This patch converts ioctl call to unlocked_ioctl form with explicit big-kernel-lock. Also it makes a bit of cleanup converting miscdevice structure initialization to C99 form.

---

Any comments are welcome.
This is untested code - i've no such chip on my laptop.

Andi, i think we could use mutex to eliminate BKL, but not sure.

 drivers/char/ip27-rtc.c |   86 ++++++++++++++++++++++++++++-------------------
 1 files changed, 51 insertions(+), 35 deletions(-)
drivers/char/ip27-rtc.c +51 -35
index 932264a..1d83e16 100644
@@ -46,8 +46,8 @@
 #include <asm/sn/sn0/hub.h>
 #include <asm/sn/sn_private.h>
 
-static int rtc_ioctl(struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg);
+static long rtc_ioctl(struct file *file, unsigned int cmd,
+		      unsigned long arg);
 
 static int rtc_read_proc(char *page, char **start, off_t off,
                          int count, int *eof, void *data);
@@ -62,7 +62,7 @@ static void get_rtc_time(struct rtc_time *rtc_tm);
 #define RTC_TIMER_ON		0x02	/* missed irq timer active	*/
 
 static unsigned char rtc_status;	/* bitmapped status byte.	*/
-static unsigned long rtc_freq;	/* Current periodic IRQ rate	*/
+static unsigned long rtc_freq;		/* Current periodic IRQ rate	*/
 static struct m48t35_rtc *rtc;
 
 /*
@@ -75,30 +75,34 @@ static unsigned long epoch = 1970;	/* year corresponding to 0x00	*/
 static const unsigned char days_in_mo[] =
 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
-static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-		     unsigned long arg)
+static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-
 	struct rtc_time wtime;
+	int err;
+
+	lock_kernel();
 
 	switch (cmd) {
 	case RTC_RD_TIME:	/* Read the time/date from RTC	*/
-	{
 		get_rtc_time(&wtime);
+		err = copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
 		break;
-	}
 	case RTC_SET_TIME:	/* Set the RTC */
 	{
 		struct rtc_time rtc_tm;
 		unsigned char mon, day, hrs, min, sec, leap_yr;
 		unsigned int yrs;
 
-		if (!capable(CAP_SYS_TIME))
-			return -EACCES;
+		if (!capable(CAP_SYS_TIME)) {
+			err = -EACCES;
+			goto unlock;
+		}
 
 		if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
-				   sizeof(struct rtc_time)))
-			return -EFAULT;
+				   sizeof(struct rtc_time))) {
+			err = -EFAULT;
+			goto unlock;
+		}
 
 		yrs = rtc_tm.tm_year + 1900;
 		mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
@@ -107,25 +111,27 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 		min = rtc_tm.tm_min;
 		sec = rtc_tm.tm_sec;
 
+		err = -EINVAL;
+
 		if (yrs < 1970)
-			return -EINVAL;
+			goto unlock;
 
 		leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
 
 		if ((mon > 12) || (day == 0))
-			return -EINVAL;
+			goto unlock;
 
 		if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
-			return -EINVAL;
+			goto unlock;
 
 		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
-			return -EINVAL;
+			goto unlock;
 
 		if ((yrs -= epoch) > 255)    /* They are unsigned */
-			return -EINVAL;
+			goto unlock;
 
 		if (yrs > 169)
-			return -EINVAL;
+			goto unlock;
 
 		if (yrs >= 100)
 			yrs -= 100;
@@ -148,12 +154,18 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 		rtc->control &= ~M48T35_RTC_SET;
 		spin_unlock_irq(&rtc_lock);
 
-		return 0;
+		err = 0;
+		break;
 	}
 	default:
-		return -EINVAL;
+		err = -EINVAL;
+		break;
 	}
-	return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+
+ unlock:
+	unlock_kernel();
+
+	return err;
 }
 
 /*
@@ -170,8 +182,8 @@ static int rtc_open(struct inode *inode, struct file *file)
 		spin_unlock_irq(&rtc_lock);
 		return -EBUSY;
 	}
-
 	rtc_status |= RTC_IS_OPEN;
+
 	spin_unlock_irq(&rtc_lock);
 
 	return 0;
@@ -197,16 +209,15 @@ static int rtc_release(struct inode *inode, struct file *file)
 
 static const struct file_operations rtc_fops = {
 	.owner		= THIS_MODULE,
-	.ioctl		= rtc_ioctl,
+	.unlocked_ioctl	= rtc_ioctl,
 	.open		= rtc_open,
 	.release	= rtc_release,
 };
 
-static struct miscdevice rtc_dev=
-{
-	RTC_MINOR,
-	"rtc",
-	&rtc_fops
+static struct miscdevice rtc_dev = {
+	.minor		= RTC_MINOR,
+	.name		= "rtc",
+	.fops		= &rtc_fops,
 };
 
 static int __init rtc_init(void)
@@ -229,16 +240,15 @@ static int __init rtc_init(void)
 
 	return 0;
 }
+module_init(rtc_init);
 
-static void __exit rtc_exit (void)
+static void __exit rtc_exit(void)
 {
 	/* interrupts and timer disabled at this point by rtc_release */
 
 	remove_proc_entry ("rtc", NULL);
 	misc_deregister(&rtc_dev);
 }
-
-module_init(rtc_init);
 module_exit(rtc_exit);
 
 /*
@@ -274,14 +284,20 @@ static int rtc_get_status(char *buf)
 }
 
 static int rtc_read_proc(char *page, char **start, off_t off,
-                                 int count, int *eof, void *data)
+			 int count, int *eof, void *data)
 {
         int len = rtc_get_status(page);
-        if (len <= off+count) *eof = 1;
+
+        if (len <= off + count)
+		*eof = 1;
+
         *start = page + off;
         len -= off;
-        if (len>count) len = count;
-        if (len<0) len = 0;
+        if (len > count)
+		len = count;
+        if (len < 0)
+		len = 0;
+
         return len;
 }
 
#1 lore raw
Quoted 11 lines · Cyrill Gorcunov

This patch converts ioctl call to unlocked_ioctl form with explicit big-kernel-lock. Also it makes a bit of cleanup converting miscdevice structure initialization to C99 form.

---

Any comments are welcome.
This is untested code - i've no such chip on my laptop.

Andi, i think we could use mutex to eliminate BKL, but not sure.

Looks like it can be dropped here. All usage of rtc-> is done under rtc_lock. Every other variable there is thread-local.

#3 lore raw
In reply to Cyrill Gorcunov
Quoted 43 lines · Cyrill Gorcunov

This patch converts ioctl call to unlocked_ioctl form with explicit big-kernel-lock. Also it makes a bit of cleanup converting miscdevice structure initialization to C99 form.

---

Any comments are welcome.
This is untested code - i've no such chip on my laptop.

Andi, i think we could use mutex to eliminate BKL, but not sure.

 drivers/char/ip27-rtc.c |   86 ++++++++++++++++++++++++++++-------------------
 1 files changed, 51 insertions(+), 35 deletions(-)
drivers/char/ip27-rtc.c +3 -3
index 932264a..1d83e16 100644
@@ -46,8 +46,8 @@
 #include <asm/sn/sn0/hub.h>
 #include <asm/sn/sn_private.h>
 
-static int rtc_ioctl(struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg);
+static long rtc_ioctl(struct file *file, unsigned int cmd,
+		      unsigned long arg);
 
 static int rtc_read_proc(char *page, char **start, off_t off,
                          int count, int *eof, void *data);
@@ -62,7 +62,7 @@ static void get_rtc_time(struct rtc_time *rtc_tm);
 #define RTC_TIMER_ON		0x02	/* missed irq timer active	*/
 
 static unsigned char rtc_status;	/* bitmapped status byte.	*/
-static unsigned long rtc_freq;	/* Current periodic IRQ rate	*/
+static unsigned long rtc_freq;		/* Current periodic IRQ rate	*/
 static struct m48t35_rtc *rtc;
 
 /*
@@ -75,30 +75,34 @@ static unsigned long epoch = 1970;	/* year corresponding to 0x00	*/
 static const unsigned char days_in_mo[] =
 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

drivers/char/ip27-rtc.c static unsigned long epoch = 1970; /* year corresponding to 0x00 */ · line 83

Quoted 10 lines
drivers/char/ip27-rtc.c +4 -3
 
-static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-		     unsigned long arg)
+static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-
 	struct rtc_time wtime;
+	int err;
+
+	lock_kernel();

This routine seems to be re-entrant due to parameters on stack + the spin lock, hence the lock is not needed here at all.

Quoted 152 lines
drivers/char/ip27-rtc.c +39 -27
 	switch (cmd) {
 	case RTC_RD_TIME:	/* Read the time/date from RTC	*/
-	{
 		get_rtc_time(&wtime);
+		err = copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
 		break;
-	}
 	case RTC_SET_TIME:	/* Set the RTC */
 	{
 		struct rtc_time rtc_tm;
 		unsigned char mon, day, hrs, min, sec, leap_yr;
 		unsigned int yrs;
 
-		if (!capable(CAP_SYS_TIME))
-			return -EACCES;
+		if (!capable(CAP_SYS_TIME)) {
+			err = -EACCES;
+			goto unlock;
+		}
 
 		if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
-				   sizeof(struct rtc_time)))
-			return -EFAULT;
+				   sizeof(struct rtc_time))) {
+			err = -EFAULT;
+			goto unlock;
+		}
 
 		yrs = rtc_tm.tm_year + 1900;
 		mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
@@ -107,25 +111,27 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 		min = rtc_tm.tm_min;
 		sec = rtc_tm.tm_sec;
 
+		err = -EINVAL;
+
 		if (yrs < 1970)
-			return -EINVAL;
+			goto unlock;
 
 		leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
 
 		if ((mon > 12) || (day == 0))
-			return -EINVAL;
+			goto unlock;
 
 		if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
-			return -EINVAL;
+			goto unlock;
 
 		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
-			return -EINVAL;
+			goto unlock;
 
 		if ((yrs -= epoch) > 255)    /* They are unsigned */
-			return -EINVAL;
+			goto unlock;
 
 		if (yrs > 169)
-			return -EINVAL;
+			goto unlock;
 
 		if (yrs >= 100)
 			yrs -= 100;
@@ -148,12 +154,18 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 		rtc->control &= ~M48T35_RTC_SET;
 		spin_unlock_irq(&rtc_lock);
 
-		return 0;
+		err = 0;
+		break;
 	}
 	default:
-		return -EINVAL;
+		err = -EINVAL;
+		break;
 	}
-	return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+
+ unlock:
+	unlock_kernel();
+
+	return err;
 }
 
 /*
@@ -170,8 +182,8 @@ static int rtc_open(struct inode *inode, struct file *file)
 		spin_unlock_irq(&rtc_lock);
 		return -EBUSY;
 	}
-
 	rtc_status |= RTC_IS_OPEN;
+
 	spin_unlock_irq(&rtc_lock);
 
 	return 0;
@@ -197,16 +209,15 @@ static int rtc_release(struct inode *inode, struct file *file)
 
 static const struct file_operations rtc_fops = {
 	.owner		= THIS_MODULE,
-	.ioctl		= rtc_ioctl,
+	.unlocked_ioctl	= rtc_ioctl,
 	.open		= rtc_open,
 	.release	= rtc_release,
 };
 
-static struct miscdevice rtc_dev=
-{
-	RTC_MINOR,
-	"rtc",
-	&rtc_fops
+static struct miscdevice rtc_dev = {
+	.minor		= RTC_MINOR,
+	.name		= "rtc",
+	.fops		= &rtc_fops,
 };
 
 static int __init rtc_init(void)
@@ -229,16 +240,15 @@ static int __init rtc_init(void)
 
 	return 0;
 }
+module_init(rtc_init);
 
-static void __exit rtc_exit (void)
+static void __exit rtc_exit(void)
 {
 	/* interrupts and timer disabled at this point by rtc_release */
 
 	remove_proc_entry ("rtc", NULL);
 	misc_deregister(&rtc_dev);
 }
-
-module_init(rtc_init);
 module_exit(rtc_exit);
 
 /*
@@ -274,14 +284,20 @@ static int rtc_get_status(char *buf)
 }
 
 static int rtc_read_proc(char *page, char **start, off_t off,
-                                 int count, int *eof, void *data)
+			 int count, int *eof, void *data)
 {
         int len = rtc_get_status(page);
-        if (len <= off+count) *eof = 1;
+
+        if (len <= off + count)
+		*eof = 1;
+
         *start = page + off;

drivers/char/ip27-rtc.c static int rtc_get_status(char *buf) · line 302

Quoted 10 lines
drivers/char/ip27-rtc.c +5 -2
         len -= off;
-        if (len>count) len = count;
-        if (len<0) len = 0;
+        if (len > count)
+		len = count;
+        if (len < 0)
+		len = 0;
+
         return len;
 }

Post these cleanup things as a separate patch, please.

regards,

Signature · 3 lines
Jiri Slaby
Faculty of Informatics, Masaryk University
Suse Labs
#2 lore raw
In reply to Alexey Dobriyan

Thanks, Alexey for review, i'll make it updated tomorrow. (sorry for top-post)

Quoted 16 lines · Alexey Dobriyan
Quoted 11 lines · Cyrill Gorcunov

This patch converts ioctl call to unlocked_ioctl form with explicit big-kernel-lock. Also it makes a bit of cleanup converting miscdevice structure initialization to C99 form.

---

Any comments are welcome.
This is untested code - i've no such chip on my laptop.

Andi, i think we could use mutex to eliminate BKL, but not sure.

Looks like it can be dropped here. All usage of rtc-> is done under rtc_lock. Every other variable there is thread-local.

#4 lore raw
In reply to Jiri Slaby
Quoted 1 line · Jiri Slaby

This routine seems to be re-entrant due to parameters on stack + the spin lock, hence the lock is not needed here at all.

s/parameters/variables/; too sleeeepy.

#13 lore raw
In reply to Cyrill Gorcunov
Quoted 5 lines · On Sunday 13 January 2008 21:32:23 Cyrill Gorcunov wrote:

This patch converts ioctl call to unlocked_ioctl form with explicit big-kernel-lock.

Also it makes a bit of cleanup
converting miscdevice structure initialization to C99 form.

First please always send cleanup patches separately. I think you have more than just the C99 conversion.

Were you even able to compile it? Since ip27 is ia64 only that would need an IA64 cross compiler or a native ia64 machine.

It's probably better to focus on drivers only that actually build on x86 :- see http://www.halobates.de/allyes for a compile log showing all buildable files for x86-64 and i386.

Other than that the patch looks ok to me, but I haven't tried to compile it either.

-Andi

#6 lore raw
In reply to Cyrill Gorcunov

This patch converts ioctl call to unlocked_ioctl form. It's possible due to rtl_lock spinlock protection.

---
The patch is *not* tested but the patch does not bring _much_ changes so it wouldn't break the compilation procedure.

If there is problem with attachment - i could send it as inline form today evening.

Andi, Jiri, Alexey the only thing I do complain about - is time set/read from several user threads that uses same (duplicated) file descriptor. Could there be useless thread spins instead of sleep (in case if this unlocked_ioctl were protected by mutex)?

 drivers/char/ip27-rtc.c |    9 ++++-----
 1 files changed, 4 insertions(+), 5 deletions(-)
drivers/char/ip27-rtc.c +4 -5
index 932264a..86e6538 100644
@@ -46,8 +46,8 @@
 #include <asm/sn/sn0/hub.h>
 #include <asm/sn/sn_private.h>
 
-static int rtc_ioctl(struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg);
+static long rtc_ioctl(struct file *filp, unsigned int cmd,
+			unsigned long arg);
 
 static int rtc_read_proc(char *page, char **start, off_t off,
                          int count, int *eof, void *data);
@@ -75,8 +75,7 @@ static unsigned long epoch = 1970;	/* year corresponding to 0x00	*/
 static const unsigned char days_in_mo[] =
 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
-static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-		     unsigned long arg)
+static long rtc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 
 	struct rtc_time wtime;
@@ -197,7 +196,7 @@ static int rtc_release(struct inode *inode, struct file *file)
 
 static const struct file_operations rtc_fops = {
 	.owner		= THIS_MODULE,
-	.ioctl		= rtc_ioctl,
+	.unlocked_ioctl	= rtc_ioctl,
 	.open		= rtc_open,
 	.release	= rtc_release,
 };
#7 lore raw
Quoted 10 lines · Cyrill Gorcunov

This patch converts ioctl call to unlocked_ioctl form. It's possible due to rtl_lock spinlock protection.

---
The patch is *not* tested but the patch does not bring _much_ changes so it wouldn't break the compilation procedure.

If there is problem with attachment - i could send it as inline form today evening.

Yes, please, especially if it is app/octet-stream (base64 encoded plaintext). Also Cc akpm or somebody who will pick your patch up.

Quoted 5 lines

Andi, Jiri, Alexey the only thing I do complain about - is time set/read from several user threads that uses same (duplicated) file descriptor. Could there be useless thread spins instead of sleep (in case if this unlocked_ioctl were protected by mutex)?

Sorry, what?

#8 lore raw

[Jiri Slaby - Mon, Jan 14, 2008 at 04:14:18PM +0100]

Quoted 10 lines · Cyrill Gorcunov
Quoted 8 lines

This patch converts ioctl call to unlocked_ioctl form. It's possible due to rtl_lock spinlock protection.

---
The patch is *not* tested but the patch does not bring _much_ changes so it wouldn't break the compilation procedure.
If there is problem with attachment - i could send it as inline form today evening.

Quoted 3 lines

Yes, please, especially if it is app/octet-stream (base64 encoded plaintext). Also Cc akpm or somebody who will pick your patch up.

ok

Quoted 8 lines
Quoted 5 lines

Andi, Jiri, Alexey the only thing I do complain about - is time set/read from several user threads that uses same (duplicated) file descriptor. Could there be useless thread spins instead of sleep (in case if this unlocked_ioctl were protected by mutex)?

Sorry, what?

Jiri, I mean rtc_open() is protected by spinlock+status from being opened simultaneously by a few processes. *But* lets imagine the following situation - this fd (file descriptor) is opened by one multithreaded application so all threads have an access to this fd. Then one thread reads rtc periodically thru unlocked_ioctl and another thread set new time from time to time. So the question I have - is it possible to get second thread stopped at attemption to get rtc spinlock while another thread is setting the new time? Or this situation never-ever could be? i'm not really familiar with process management in Linux and as result could be wrong.

- Cyrill -

#9 lore raw
Quoted 10 lines · Cyrill Gorcunov

Jiri, I mean rtc_open() is protected by spinlock+status from being opened simultaneously by a few processes. *But* lets imagine the following situation - this fd (file descriptor) is opened by one multithreaded application so all threads have an access to this fd. Then one thread reads rtc periodically thru unlocked_ioctl and another thread set new time from time to time. So the question I have - is it possible to get second thread stopped at attemption to get rtc spinlock while another thread is setting the new time? Or this situation never-ever could be? i'm not really familiar with process management in Linux and as result could be wrong.

Access to global variable 'rtc' (the rtc itself) is serialized through the spinlock, I see no problem there. If you call read-read-write-read from 4 tasks in userspace, it might be _still_ (no change) reordered to e.g. write-read-read-read by the scheduler.

In fact, the reading process is stopped while the another one is writing the time (due to spinlock).

#10 lore raw

[Jiri Slaby - Mon, Jan 14, 2008 at 04:59:22PM +0100]

Quoted 12 lines · Cyrill Gorcunov
Quoted 10 lines

Jiri, I mean rtc_open() is protected by spinlock+status from being opened simultaneously by a few processes. *But* lets imagine the following situation - this fd (file descriptor) is opened by one multithreaded application so all threads have an access to this fd. Then one thread reads rtc periodically thru unlocked_ioctl and another thread set new time from time to time. So the question I have - is it possible to get second thread stopped at attemption to get rtc spinlock while another thread is setting the new time? Or this situation never-ever could be? i'm not really familiar with process management in Linux and as result could be wrong.

Quoted 8 lines

Access to global variable 'rtc' (the rtc itself) is serialized through the spinlock, I see no problem there. If you call read-read-write-read from 4 tasks in userspace, it might be _still_ (no change) reordered to e.g. write-read-read-read by the scheduler.

In fact, the reading process is stopped while the another one is writing the time (due to spinlock).

Yes, process would be stopped, and not *just* stopped but could spend all his cpu time-slice in attempt to get spinlock (espec if set time is much longer than read), but if we use mutex here the process could just sleep instead of trying to get spinlock granted. Am I wrong? Or this is not worth to do it?

- Cyrill -

#11 lore raw
Quoted 5 lines · Cyrill Gorcunov

Yes, process would be stopped, and not *just* stopped but could spend all his cpu time-slice in attempt to get spinlock (espec if set time is much longer than read), but if we use mutex here the process could just sleep instead of trying to get spinlock granted. Am I wrong? Or this is not worth to do it?

I would say no. It'll spin only for nanoseconds there.

#12 lore raw

[Jiri Slaby - Mon, Jan 14, 2008 at 05:27:00PM +0100]

Quoted 9 lines · Cyrill Gorcunov
Quoted 5 lines

Yes, process would be stopped, and not *just* stopped but could spend all his cpu time-slice in attempt to get spinlock (espec if set time is much longer than read), but if we use mutex here the process could just sleep instead of trying to get spinlock granted. Am I wrong? Or this is not worth to do it?

I would say no. It'll spin only for nanoseconds there.

Thanks a lot Jiri, I'll resend improved patch.

- Cyrill -