Usporedba prema "02-simple-device" (napravljen je diff, ali onda maknuto ono što ne prikazuje novosti; ne može se ovaj diff ovako aplicirati - mnoge stvari nedostaju!) u config.h ----------- +#define MAX_MSGS 16 +#define MAX_MSG_SIZE 64 +#define MAX_THREADS 5 +struct msg { + struct list_head list; //poruke ce biti u listi + size_t size; + char d[1]; //prvi bajt poruke, ostali neposredno iza +}; + +struct msg_queue { + size_t max_msgs; + size_t msg_cnt; + size_t max_msg_size; + size_t max_threads; + size_t thread_cnt; + + struct list_head msgs; //lista za poruke + struct semaphore readers; //blokirani citaci + struct semaphore writers; //blokirani pisaci + struct mutex lock; //za medjusobno iskljucivanje }; /* Device driver */ struct shofer_dev { + struct msg_queue msg_queue; /* red poruka */ }; + u shofer.c ----------- +static int max_msgs = MAX_MSGS; +static int max_msg_size = MAX_MSG_SIZE; +static int max_threads = MAX_THREADS; + +module_param(max_msgs, int, S_IRUGO); +MODULE_PARM_DESC(max_msgs, "Maximal number of messages in a queue"); +module_param(max_msg_size, int, S_IRUGO); +MODULE_PARM_DESC(max_msg_size, "Maximal message size"); +module_param(max_threads, int, S_IRUGO); +MODULE_PARM_DESC(max_threads, "Maximal number of threads simultaneously using message queue"); +void msgq_init(struct msg_queue *msg_queue, + size_t max_msgs, size_t max_msg_size, size_t max_threads) +{ + msg_queue->max_msgs = max_msgs; + msg_queue->msg_cnt = 0; + msg_queue->max_msg_size = max_msg_size; + msg_queue->max_threads = max_threads; + msg_queue->thread_cnt = 0; + INIT_LIST_HEAD(&msg_queue->msgs); + sema_init(&msg_queue->readers, 0); + sema_init(&msg_queue->writers, max_msgs); + mutex_init(&msg_queue->lock); +} + +static void msgq_delete(struct msg_queue *msg_queue) +{ + //todo - obrisi sve poruke ako ih ima + struct msg *msg, *m; + list_for_each_entry_safe (msg, m, &msg_queue->msgs, list) { + list_del (&msg->list); + kfree(msg); + } } /* Create and initialize a single shofer_dev */ static struct shofer_dev *shofer_create(dev_t dev_no, + struct file_operations *fops, int *retval) { + /* initialize the message queue */ + msgq_init(&shofer->msg_queue, max_msgs, max_msg_size, max_threads); + return shofer; } static void shofer_delete(struct shofer_dev *shofer) { + msgq_delete(&shofer->msg_queue); } +/* Called when the process calls "open" on this device */ static int shofer_open(struct inode *inode, struct file *filp) { + if (shofer->msg_queue.thread_cnt == shofer->msg_queue.max_threads) + return -EBUSY; + + shofer->msg_queue.thread_cnt++; + return 0; } /* Called when a process performs "close" operation */ static int shofer_release(struct inode *inode, struct file *filp) { + shofer->msg_queue.thread_cnt--; + + return 0; +} + +/* Uzmi poruku iz reda */ static ssize_t shofer_read(struct file *filp, char __user *ubuf, size_t count, loff_t *f_pos /* ignoring f_pos */) { + struct msg_queue *msg_queue = &shofer->msg_queue; + struct msg *msg; + + if ( (filp->f_flags & O_ACCMODE) != O_RDONLY) + return -EPERM; + + if (count < msg_queue->max_msg_size) + return -EFBIG; + + if (down_interruptible(&msg_queue->readers)) //ima li poruka? ako ne čekaj + return -ERESTARTSYS; //čekanje prekinuto signalom + + //uđi u KO + if (mutex_lock_interruptible(&msg_queue->lock)) { + //čekanje na ulaz u KO prekinuto signalom + up(&msg_queue->readers); //nismo uzeli poruku return -ERESTARTSYS; + } + + //ima poruka, uzmi prvu + msg = list_first_entry_or_null(&msg_queue->msgs, struct msg, list); + if (msg == NULL) { + klog(KERN_WARNING, "No messages in queue! Something is wrong!\n"); + mutex_unlock(&msg_queue->lock); + return -ENOENT; + } + + list_del(&msg->list); //makni iz liste + msg_queue->msg_cnt--; + + mutex_unlock(&msg_queue->lock); //izađi iz KO + + if (!copy_to_user(ubuf, &msg->d[0], msg->size)) { + retval = msg->size; + } + else { + klog(KERN_WARNING, "copy_to_user failed"); + retval = -EFAULT; + } + kfree(msg); + up(&msg_queue->writers); //makli jednu poruku, može nova u red return retval; } +/* Stavi poruku u red */ static ssize_t shofer_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *f_pos /* ignoring f_pos */) { + struct msg_queue *msg_queue = &shofer->msg_queue; + struct msg *msg; + + if ( (filp->f_flags & O_ACCMODE) != O_WRONLY) + return -EPERM; + + if (count > msg_queue->max_msg_size) + return -EFBIG; + + if (down_interruptible(&msg_queue->writers)) //ima li mjesta za poruku? ako ne čekaj return -ERESTARTSYS; + //napravi poruku + msg = kmalloc(sizeof(struct msg) + count - 1, GFP_KERNEL); + if (!msg){ + klog(KERN_WARNING, "kmalloc failed\n"); + up(&msg_queue->writers); + return -ENOMEM; + } + + msg->size = count; + + if (copy_from_user(&msg->d[0], ubuf, msg->size)) { + klog(KERN_WARNING, "copy_from_user failed"); + kfree(msg); + up(&msg_queue->writers); + return -EFAULT; + } + + //uđi u KO + if (mutex_lock_interruptible(&msg_queue->lock)) { + kfree(msg); + up(&msg_queue->writers); + return -ERESTARTSYS; + } + + //stavi poruku na kraj liste + list_add_tail(&msg->list, &msg_queue->msgs); + msg_queue->msg_cnt++; + + mutex_unlock(&msg_queue->lock); //izađi iz KO + + up(&msg_queue->readers); + + return count; +} Programi za testiranje: citac.c -------- #include #include #include #include #include #include #include #define RED "/dev/shofer" #define MAXSZ 64 int main(int argc, char *argv[]) { int fp; char buffer[MAXSZ]; size_t size; long pid = (long) getpid(); fp = open(RED, O_RDONLY); if (fp == -1) { perror("Nisam otvorio cjevovod! Greska: "); return -1; } while(1) { memset(buffer, 0, MAXSZ); printf("Citac %ld poziva read\n", pid); size = read(fp, buffer, MAXSZ); if (size == -1) { perror("Greska pri citanju! Greska: "); break; } printf("Citac %ld procitao (%ld): %s\n", pid, size, buffer); sleep(1); } return -1; } pisac.c ------- #include #include #include #include #include #include #include #define RED "/dev/shofer" #define MAXSZ 64 void gen_text(char *text, size_t size) { int i; for (i = 0; i < size; i++) text[i] = 'A' + random() % ('Z' - 'A'); } int main(int argc, char *argv[]) { int fp; char buffer[MAXSZ]; size_t size; long pid = (long) getpid(); fp = open(RED, O_WRONLY); if (fp == -1) { perror("Nisam otvorio cjevovod! Greska: "); return -1; } srandom(pid); while(1) { memset(buffer, 0, MAXSZ); size = MAXSZ / 3 + random() % (2 * MAXSZ / 3); gen_text(buffer, size); printf("Pisac %ld poziva write s tekstom (%ld): %s\n", pid, size, buffer); size = write(fp, buffer, size); if (size == -1) { perror("Greska pri pisanju! Greska: "); break; } printf("Pisac %ld poslao (%ld): %s\n", pid, size, buffer); sleep(2); } return -1; }