1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/vnode.h> 31 #include <sys/kmem.h> 32 #include <fs/fs_subr.h> 33 #include <sys/proc.h> 34 #include <sys/kstat.h> 35 #include <sys/port_impl.h> 36 37 /* local functions */ 38 static int port_open(struct vnode **, int, cred_t *); 39 static int port_close(struct vnode *, int, int, offset_t, cred_t *); 40 static int port_getattr(struct vnode *, struct vattr *, int, cred_t *); 41 static int port_access(struct vnode *, int, int, cred_t *); 42 static int port_realvp(vnode_t *, vnode_t **); 43 static int port_poll(vnode_t *, short, int, short *, struct pollhead **); 44 static void port_inactive(struct vnode *, cred_t *); 45 46 const fs_operation_def_t port_vnodeops_template[] = { 47 VOPNAME_OPEN, port_open, 48 VOPNAME_CLOSE, port_close, 49 VOPNAME_GETATTR, port_getattr, 50 VOPNAME_ACCESS, port_access, 51 VOPNAME_INACTIVE, (fs_generic_func_p) port_inactive, 52 VOPNAME_FRLOCK, fs_error, 53 VOPNAME_REALVP, port_realvp, 54 VOPNAME_POLL, (fs_generic_func_p) port_poll, 55 VOPNAME_PATHCONF, fs_error, 56 VOPNAME_DISPOSE, fs_error, 57 VOPNAME_GETSECATTR, fs_error, 58 VOPNAME_SHRLOCK, fs_error, 59 NULL, NULL 60 }; 61 62 /* ARGSUSED */ 63 static int 64 port_open(struct vnode **vpp, int flag, cred_t *cr) 65 { 66 return (0); 67 } 68 69 /* 70 * port_discard_events() scans the port event queue for events owned 71 * by current proc. Non-shareable events will be discarded, all other 72 * events remain in the event queue. 73 */ 74 void 75 port_discard_events(port_queue_t *portq) 76 { 77 port_kevent_t *kevp; 78 pid_t pid = curproc->p_pid; 79 80 /* 81 * The call to port_block() is required to avoid interaction 82 * with other threads in port_get(n). 83 */ 84 mutex_enter(&portq->portq_mutex); 85 port_block(portq); 86 port_push_eventq(portq); /* empty temporary queue */ 87 kevp = list_head(&portq->portq_list); 88 while (kevp) { 89 if (kevp->portkev_pid == pid) { 90 /* own event, check if it is shareable */ 91 if (kevp->portkev_flags & PORT_KEV_NOSHARE) 92 kevp->portkev_flags |= PORT_KEV_FREE; 93 } 94 kevp = list_next(&portq->portq_list, kevp); 95 } 96 port_unblock(portq); 97 mutex_exit(&portq->portq_mutex); 98 } 99 100 /* 101 * Called from port_close(). 102 * Free all kernel events structures which are still in the event queue. 103 */ 104 static void 105 port_close_events(port_queue_t *portq) 106 { 107 port_kevent_t *pkevp; 108 int events; /* ignore events */ 109 110 mutex_enter(&portq->portq_mutex); 111 while (pkevp = list_head(&portq->portq_list)) { 112 portq->portq_nent--; 113 list_remove(&portq->portq_list, pkevp); 114 if (pkevp->portkev_callback) { 115 (void) (*pkevp->portkev_callback)(pkevp->portkev_arg, 116 &events, pkevp->portkev_pid, PORT_CALLBACK_CLOSE, 117 pkevp); 118 } 119 mutex_exit(&portq->portq_mutex); 120 port_free_event_local(pkevp, 0); 121 mutex_enter(&portq->portq_mutex); 122 } 123 124 /* 125 * Wait for any thread in pollwakeup(), accessing this port to 126 * finish. 127 */ 128 while (portq->portq_flags & PORTQ_POLLWK_PEND) { 129 cv_wait(&portq->portq_closecv, &portq->portq_mutex); 130 } 131 mutex_exit(&portq->portq_mutex); 132 } 133 134 /* 135 * The port_close() function is called from standard close(2) when 136 * the file descriptor is of type S_IFPORT/VPORT. 137 * Port file descriptors behave like standard file descriptors. It means, 138 * the port file/vnode is only destroyed on last close. 139 * If the reference counter is > 1 then 140 * - sources associated with the port will be notified about the close, 141 * - objects associated with the port will be dissociated, 142 * - pending and delivered events will be discarded. 143 * On last close all references and caches will be removed. The vnode itself 144 * will be destroyed with VOP_RELE(). 145 */ 146 /* ARGSUSED */ 147 static int 148 port_close(struct vnode *vp, int flag, int count, offset_t offset, cred_t *cr) 149 { 150 port_t *pp; 151 port_queue_t *portq; 152 port_source_t *ps; 153 port_source_t *ps_next; 154 int source; 155 156 pp = VTOEP(vp); 157 mutex_enter(&pp->port_mutex); 158 if (pp->port_flags & PORT_CLOSED) { 159 mutex_exit(&pp->port_mutex); 160 return (0); 161 } 162 mutex_exit(&pp->port_mutex); 163 164 portq = &pp->port_queue; 165 if (count > 1) { 166 /* 167 * It is not the last close. 168 * Remove/free all event resources owned by the current proc 169 * First notify all with the port associated sources about the 170 * close(2). The last argument of the close callback function 171 * advises the source about the type of of the close. 172 * If the port was set in alert mode by the curren process then 173 * remove the alert mode. 174 */ 175 176 /* check alert mode of the port */ 177 mutex_enter(&portq->portq_mutex); 178 if ((portq->portq_flags & PORTQ_ALERT) && 179 (portq->portq_alert.portal_pid == curproc->p_pid)) 180 portq->portq_flags &= ~PORTQ_ALERT; 181 mutex_exit(&portq->portq_mutex); 182 183 /* notify all event sources about port_close() */ 184 mutex_enter(&portq->portq_source_mutex); 185 for (source = 0; source < PORT_SCACHE_SIZE; source++) { 186 ps = portq->portq_scache[PORT_SHASH(source)]; 187 for (; ps != NULL; ps = ps->portsrc_next) { 188 if (ps->portsrc_close != NULL) 189 (*ps->portsrc_close) 190 (ps->portsrc_closearg, pp->port_fd, 191 curproc->p_pid, 0); 192 } 193 } 194 mutex_exit(&portq->portq_source_mutex); 195 port_discard_events(&pp->port_queue); 196 return (0); 197 } 198 199 /* 200 * We are executing the last close of the port -> discard everything 201 * Make sure that all threads/processes accessing this port leave 202 * the kernel immediately. 203 */ 204 205 mutex_enter(&portq->portq_mutex); 206 portq->portq_flags |= PORTQ_CLOSE; 207 while (portq->portq_thrcnt > 0) { 208 if (portq->portq_thread != NULL) 209 cv_signal(&portq->portq_thread->portget_cv); 210 cv_wait(&portq->portq_closecv, &portq->portq_mutex); 211 } 212 mutex_exit(&portq->portq_mutex); 213 214 /* 215 * Send "last close" message to associated sources. 216 * - new event allocation requests are being denied since uf_file entry 217 * was set to NULL in closeandsetf(). 218 * - all still allocated event structures must be returned to the 219 * port immediately: 220 * - call port_free_event(*event) or 221 * - call port_send_event(*event) to complete event operations 222 * which need activities in a dedicated process environment. 223 * The port_close() function waits until all allocated event structures 224 * are delivered back to the port. 225 */ 226 227 mutex_enter(&portq->portq_source_mutex); 228 for (source = 0; source < PORT_SCACHE_SIZE; source++) { 229 ps = portq->portq_scache[PORT_SHASH(source)]; 230 for (; ps != NULL; ps = ps_next) { 231 ps_next = ps->portsrc_next; 232 if (ps->portsrc_close != NULL) 233 (*ps->portsrc_close)(ps->portsrc_closearg, 234 pp->port_fd, curproc->p_pid, 1); 235 kmem_free(ps, sizeof (port_source_t)); 236 } 237 } 238 kmem_free(portq->portq_scache, 239 PORT_SCACHE_SIZE * sizeof (port_source_t *)); 240 portq->portq_scache = NULL; 241 mutex_exit(&portq->portq_source_mutex); 242 243 mutex_enter(&portq->portq_mutex); 244 /* Wait for outstanding events */ 245 while (pp->port_curr > portq->portq_nent) 246 cv_wait(&portq->portq_closecv, &portq->portq_mutex); 247 mutex_exit(&portq->portq_mutex); 248 249 /* 250 * If PORT_SOURCE_FD objects were not associated with the port then 251 * it is necessary to free the port_fdcache structure here. 252 */ 253 254 if (portq->portq_pcp != NULL) { 255 mutex_destroy(&portq->portq_pcp->pc_lock); 256 kmem_free(portq->portq_pcp, sizeof (port_fdcache_t)); 257 portq->portq_pcp = NULL; 258 } 259 260 /* 261 * Now all events are passed back to the port, 262 * discard remaining events in the port queue 263 */ 264 265 port_close_events(portq); 266 return (0); 267 } 268 269 /* 270 * The port_poll() function is the VOP_POLL() entry of event ports. 271 * Event ports return: 272 * POLLIN : events are available in the event queue 273 * POLLOUT : event queue can still accept events 274 */ 275 /*ARGSUSED*/ 276 static int 277 port_poll(vnode_t *vp, short events, int anyyet, short *reventsp, 278 struct pollhead **phpp) 279 { 280 port_t *pp; 281 port_queue_t *portq; 282 short levents; 283 284 pp = VTOEP(vp); 285 portq = &pp->port_queue; 286 levents = 0; 287 mutex_enter(&portq->portq_mutex); 288 if (portq->portq_nent) 289 levents = POLLIN; 290 if (pp->port_curr < pp->port_max_events) 291 levents |= POLLOUT; 292 levents &= events; 293 *reventsp = levents; 294 if (levents == 0) { 295 if (!anyyet) { 296 *phpp = &pp->port_pollhd; 297 portq->portq_flags |= 298 events & POLLIN ? PORTQ_POLLIN : 0; 299 portq->portq_flags |= 300 events & POLLOUT ? PORTQ_POLLOUT : 0; 301 } 302 } 303 mutex_exit(&portq->portq_mutex); 304 return (0); 305 } 306 307 308 /* ARGSUSED */ 309 static int 310 port_getattr(struct vnode *vp, struct vattr *vap, int flags, cred_t *cr) 311 { 312 port_t *pp; 313 extern dev_t portdev; 314 315 pp = VTOEP(vp); 316 317 vap->va_mask = 0; /* bit-mask of attributes */ 318 vap->va_type = vp->v_type; /* vnode type (for create) */ 319 vap->va_mode = 0; /* file access mode */ 320 vap->va_uid = pp->port_uid; /* owner user id */ 321 vap->va_gid = pp->port_gid; /* owner group id */ 322 vap->va_fsid = portdev; /* file system id */ 323 vap->va_nodeid = (ino64_t)0; /* node id */ 324 vap->va_nlink = vp->v_count; /* number of references to file */ 325 vap->va_size = (u_offset_t)pp->port_queue.portq_nent; /* file size */ 326 vap->va_atime = pp->port_ctime; /* time of last access */ 327 vap->va_mtime = pp->port_ctime; /* time of last modification */ 328 vap->va_ctime = pp->port_ctime; /* time file ``created'' */ 329 vap->va_rdev = portdev; /* device the file represents */ 330 vap->va_blksize = 0; /* fundamental block size */ 331 vap->va_nblocks = (fsblkcnt64_t)0; /* # of blocks allocated */ 332 vap->va_seq = 0; /* sequence number */ 333 334 return (0); 335 } 336 337 /* 338 * Destroy the port. 339 */ 340 /* ARGSUSED */ 341 static void 342 port_inactive(struct vnode *vp, cred_t *cr) 343 { 344 port_t *pp = VTOEP(vp); 345 extern port_kstat_t port_kstat; 346 347 mutex_enter(&port_control.pc_mutex); 348 port_control.pc_nents--; 349 curproc->p_portcnt--; 350 port_kstat.pks_ports.value.ui32--; 351 mutex_exit(&port_control.pc_mutex); 352 vn_free(vp); 353 mutex_destroy(&pp->port_mutex); 354 mutex_destroy(&pp->port_queue.portq_mutex); 355 mutex_destroy(&pp->port_queue.portq_source_mutex); 356 kmem_free(pp, sizeof (port_t)); 357 } 358 359 /* ARGSUSED */ 360 static int 361 port_access(struct vnode *vp, int mode, int flags, cred_t *cr) 362 { 363 return (0); 364 } 365 366 /* ARGSUSED */ 367 static int 368 port_realvp(vnode_t *vp, vnode_t **vpp) 369 { 370 *vpp = vp; 371 return (0); 372 } 373